mirror of https://github.com/Revertron/Alfis
First correct working serialization.
commit
f46367c95d
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
**/*.rs.bk
|
@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "Wyrd"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Revertron <rev@revertron.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rust-crypto = "^0.2"
|
||||||
|
num_cpus = "1.10.1"
|
||||||
|
byteorder = "1.3.2"
|
||||||
|
serde = { version = "1.0.102", features = ["derive"] }
|
||||||
|
serde_json = "1.0.42"
|
||||||
|
num-bigint = "0.2"
|
||||||
|
num-traits = "0.2"
|
||||||
|
bincode = "1.2.0"
|
||||||
|
groestl = "0.8.0"
|
||||||
|
base64 = "0.11.0"
|
||||||
|
chrono = "0.4.9"
|
||||||
|
rand = "0.7.2"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
serde_bytes = "0.11.2"
|
||||||
|
serde_derive = "1.0.27"
|
@ -0,0 +1,81 @@
|
|||||||
|
extern crate serde;
|
||||||
|
extern crate serde_json;
|
||||||
|
extern crate num_bigint;
|
||||||
|
extern crate num_traits;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use chrono::{Utc, DateTime};
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use num_bigint::BigUint;
|
||||||
|
use num_traits::One;
|
||||||
|
use crypto::sha2::Sha512;
|
||||||
|
use crypto::digest::Digest;
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
|
||||||
|
pub struct Block {
|
||||||
|
pub index: u64,
|
||||||
|
pub timestamp: i64,
|
||||||
|
pub chain_id: u32,
|
||||||
|
pub version: u32,
|
||||||
|
pub difficulty: usize,
|
||||||
|
pub random: u32,
|
||||||
|
pub nonce: u64,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub transaction: Option<Transaction>,
|
||||||
|
pub prev_block_hash: Hash,
|
||||||
|
#[serde(skip_serializing_if = "Hash::is_default")]
|
||||||
|
pub hash: Hash,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Block {
|
||||||
|
pub fn new(index: u64, timestamp: i64, chain_id: u32, version: u32, prev_block_hash: Hash, transaction: Option<Transaction>) -> Self {
|
||||||
|
Block {
|
||||||
|
index,
|
||||||
|
timestamp,
|
||||||
|
chain_id,
|
||||||
|
version,
|
||||||
|
difficulty: 18,
|
||||||
|
random: 0,
|
||||||
|
nonce: 0,
|
||||||
|
transaction,
|
||||||
|
prev_block_hash,
|
||||||
|
hash: Hash::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mine(&mut self) {
|
||||||
|
self.random = rand::random();
|
||||||
|
let data = serde_json::to_string(&self).unwrap();
|
||||||
|
println!("Mining block:\n{}", data);
|
||||||
|
for nonce_attempt in 0..std::u64::MAX {
|
||||||
|
self.nonce = nonce_attempt;
|
||||||
|
self.timestamp = Utc::now().timestamp();
|
||||||
|
let hash = Self::hash(serde_json::to_string(&self).unwrap().as_bytes());
|
||||||
|
if hash_is_good(&hash.as_bytes(), self.difficulty) {
|
||||||
|
self.hash = hash;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hash(data: &[u8]) -> Hash {
|
||||||
|
let mut buf: [u8; 64] = [0; 64];
|
||||||
|
let mut digest = Sha512::new();
|
||||||
|
digest.input(data);
|
||||||
|
digest.result(&mut buf);
|
||||||
|
Hash::from_vec(&buf.to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_genesis(&self) -> bool {
|
||||||
|
self.index == 0 && self.transaction.is_none() && self.prev_block_hash == Hash::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,68 @@
|
|||||||
|
use crate::{Block, Transaction, Hash};
|
||||||
|
use chrono::{Utc, DateTime};
|
||||||
|
|
||||||
|
pub struct Blockchain {
|
||||||
|
pub chain_id: u32,
|
||||||
|
pub version: u32,
|
||||||
|
pub blocks: Vec<Block>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Blockchain {
|
||||||
|
pub fn new(chain_id: u32, version: u32) -> Self {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_block(&self, transaction: Transaction) -> Block {
|
||||||
|
let prev_block = self.blocks.last().unwrap();
|
||||||
|
let block = Block::new(prev_block.index + 1,Utc::now().timestamp(), self.chain_id, self.version, prev_block.hash.clone(), Some(transaction));
|
||||||
|
block
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn genesis(chain_id: u32, version: u32) -> Block {
|
||||||
|
Block::new(0, Utc::now().timestamp(), chain_id, version, Hash::default(), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_block(&mut self, block: Block) {
|
||||||
|
if self.check_block(&block, None) {
|
||||||
|
println!("Adding block:\n{:?}", &block);
|
||||||
|
self.blocks.push(block);
|
||||||
|
} else {
|
||||||
|
println!("Bad block found, ignoring:\n{:?}", &block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check(&self) -> bool {
|
||||||
|
let mut prev_block = None;
|
||||||
|
for block in self.blocks.iter() {
|
||||||
|
if !self.check_block(block, prev_block) {
|
||||||
|
println!("Block {:?} is bad", block);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
prev_block = Some(block);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_block(&self, block: &Block, prev_block: Option<&Block>) -> bool {
|
||||||
|
if !Self::check_block_hash(block) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if prev_block.is_none() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return block.prev_block_hash == prev_block.unwrap().hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_block_hash(block: &Block) -> bool {
|
||||||
|
// We need to clear Hash value to rehash it without it for check :(
|
||||||
|
let mut copy: Block = block.clone();
|
||||||
|
copy.hash = Hash::default();
|
||||||
|
let data = serde_json::to_string(©).unwrap();
|
||||||
|
Block::hash(data.as_bytes()) == block.hash
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,121 @@
|
|||||||
|
use crate::utils;
|
||||||
|
use std::cmp::min;
|
||||||
|
use serde::{Serialize, Deserialize, Serializer, Deserializer};
|
||||||
|
use std::fmt;
|
||||||
|
use serde::de::{Error as DeError, Visitor};
|
||||||
|
use serde::ser::SerializeSeq;
|
||||||
|
use serde::export::Formatter;
|
||||||
|
use serde::export::fmt::Error;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
/// A hash consisting of all zeroes, used as a constant
|
||||||
|
pub const ZERO_HASH: Hash = Hash{ bytes: [0u8; 64]};
|
||||||
|
|
||||||
|
/// A hash struct
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Hash {
|
||||||
|
bytes: [u8; 64]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash {
|
||||||
|
/// Size of a hash in bytes.
|
||||||
|
const LEN: usize = 64;
|
||||||
|
|
||||||
|
pub fn new(bytes: [u8; 64]) -> Self {
|
||||||
|
Hash{bytes}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds a Hash from a byte vector. If the vector is too short, it will be
|
||||||
|
/// completed by zeroes. If it's too long, it will be truncated.
|
||||||
|
pub fn from_vec(v: &[u8]) -> Hash {
|
||||||
|
let mut h = [0; Hash::LEN];
|
||||||
|
let copy_size = min(v.len(), Hash::LEN);
|
||||||
|
h[..copy_size].copy_from_slice(&v[..copy_size]);
|
||||||
|
Hash::new(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the hash to a byte vector
|
||||||
|
pub fn to_vec(&self) -> Vec<u8> {
|
||||||
|
self.bytes.to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a byte slice of the hash contents.
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
&self.bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a hash to hex string format.
|
||||||
|
pub fn to_hex(&self) -> String {
|
||||||
|
utils::to_hex(self.to_vec().as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_default(&self) -> bool {
|
||||||
|
utils::same_hash(&self.bytes, &Hash::default().bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Hash {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where
|
||||||
|
S: Serializer {
|
||||||
|
serializer.serialize_str(&crate::utils::to_hex(&self.bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HashVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for HashVisitor {
|
||||||
|
type Value = Hash;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut Formatter) -> Result<(), Error> {
|
||||||
|
formatter.write_str("64 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> where E: DeError, {
|
||||||
|
if v.len() == Hash::LEN {
|
||||||
|
let mut h = [0; Hash::LEN];
|
||||||
|
let copy_size = min(v.len(), Hash::LEN);
|
||||||
|
h[..copy_size].copy_from_slice(&v[..copy_size]);
|
||||||
|
Ok(Hash::new(h))
|
||||||
|
} else {
|
||||||
|
Err(E::custom("Hash must be 64 bytes!"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'dd> Deserialize<'dd> for Hash {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'dd>>::Error> where
|
||||||
|
D: Deserializer<'dd> {
|
||||||
|
deserializer.deserialize_bytes(HashVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Hash {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
utils::same_hash(&self.bytes, &other.bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ne(&self, other: &Self) -> bool {
|
||||||
|
!utils::same_hash(&self.bytes, &other.bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for Hash {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
&self.bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Hash {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let hash_hex = self.to_hex();
|
||||||
|
const NUM_SHOW: usize = 8;
|
||||||
|
|
||||||
|
write!(f, "{}", &hash_hex[..NUM_SHOW])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Hash {
|
||||||
|
fn default() -> Hash {
|
||||||
|
ZERO_HASH
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,204 @@
|
|||||||
|
extern crate crypto;
|
||||||
|
extern crate serde;
|
||||||
|
use crypto::ed25519::{keypair, signature, verify};
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
use std::fs;
|
||||||
|
use std::fmt;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::io::Error as IoError;
|
||||||
|
use serde::export::fmt::Error;
|
||||||
|
use serde::{Serialize, Deserialize, Serializer, Deserializer};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Signature {
|
||||||
|
private_key: KeyPrivate,
|
||||||
|
public_key: KeyPublic,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Signature {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut buf = [0u8; 64];
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
rng.fill(&mut buf);
|
||||||
|
let (private, public) = keypair(&buf);
|
||||||
|
Signature {private_key: KeyPrivate::new(&private), public_key: KeyPublic::new(&public)}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes(seed: &[u8]) -> Self {
|
||||||
|
let (private, public) = keypair(&seed);
|
||||||
|
Signature {private_key: KeyPrivate::new(&private), public_key: KeyPublic::new(&public)}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_file(filename: &str, _password: &str) -> Option<Self> {
|
||||||
|
match fs::read(&Path::new(filename)) {
|
||||||
|
Ok(key) => {
|
||||||
|
Some(Self::from_bytes(key.as_slice()))
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_public(&self) -> KeyPublic {
|
||||||
|
self.public_key.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_private(&self) -> KeyPrivate {
|
||||||
|
self.private_key.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sign(&self, message: &[u8]) -> [u8; 64] {
|
||||||
|
signature(message, &self.private_key.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check(&self, message: &[u8], public_key: &[u8], signature: &[u8]) -> bool {
|
||||||
|
verify(message, &self.public_key.data, signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*impl fmt::Debug for Signature {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt.debug_struct("Signature")
|
||||||
|
.field("pub", &&self.public_key[..])
|
||||||
|
.field("priv", &&self.private_key[..])
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct KeyPublic {
|
||||||
|
data: [u8; 32]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyPublic {
|
||||||
|
pub fn new(data: &[u8]) -> Self {
|
||||||
|
let mut buf = [0u8; 32];
|
||||||
|
buf.copy_from_slice(data);
|
||||||
|
KeyPublic{ data: buf }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length(&self) -> usize {
|
||||||
|
self.data.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for KeyPublic {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
crate::utils::same_hash(&self.data, &other.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ne(&self, other: &Self) -> bool {
|
||||||
|
!crate::utils::same_hash(&self.data, &other.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for KeyPublic {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt.write_str(&crate::utils::to_hex(&self.data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for KeyPublic {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where
|
||||||
|
S: Serializer {
|
||||||
|
serializer.serialize_str(&crate::utils::to_hex(&self.data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct KeyPrivate {
|
||||||
|
data: [u8; 64]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyPrivate {
|
||||||
|
pub fn new(data: &[u8]) -> Self {
|
||||||
|
let mut buf = [0u8; 64];
|
||||||
|
buf.copy_from_slice(data);
|
||||||
|
KeyPrivate{ data: buf }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length(&self) -> usize {
|
||||||
|
self.data.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for KeyPrivate {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
crate::utils::same_hash(&self.data, &other.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ne(&self, other: &Self) -> bool {
|
||||||
|
!crate::utils::same_hash(&self.data, &other.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for KeyPrivate {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt.write_str(&crate::utils::to_hex(&self.data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for KeyPrivate {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where
|
||||||
|
S: Serializer {
|
||||||
|
serializer.serialize_str(&crate::utils::to_hex(&self.data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use serde::de::{Error as DeError, Visitor};
|
||||||
|
use serde::export::Formatter;
|
||||||
|
|
||||||
|
struct PublicVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for PublicVisitor {
|
||||||
|
type Value = KeyPublic;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut Formatter) -> Result<(), Error> {
|
||||||
|
formatter.write_str("32 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> where E: DeError, {
|
||||||
|
if v.len() == 32 {
|
||||||
|
let mut h = [0; 32];
|
||||||
|
h[..32].copy_from_slice(&v[..32]);
|
||||||
|
Ok(KeyPublic::new(&h))
|
||||||
|
} else {
|
||||||
|
Err(E::custom("KeyPublic must be 32 bytes!"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'dd> Deserialize<'dd> for KeyPublic {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'dd>>::Error> where
|
||||||
|
D: Deserializer<'dd> {
|
||||||
|
deserializer.deserialize_bytes(PublicVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PrivateVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for PrivateVisitor {
|
||||||
|
type Value = KeyPrivate;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut Formatter) -> Result<(), Error> {
|
||||||
|
formatter.write_str("32 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> where E: DeError, {
|
||||||
|
if v.len() == 64 {
|
||||||
|
let mut h = [0; 64];
|
||||||
|
h[..64].copy_from_slice(&v[..64]);
|
||||||
|
Ok(KeyPrivate::new(&h))
|
||||||
|
} else {
|
||||||
|
Err(E::custom("KeyPrivate must be 64 bytes!"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'dd> Deserialize<'dd> for KeyPrivate {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'dd>>::Error> where
|
||||||
|
D: Deserializer<'dd> {
|
||||||
|
deserializer.deserialize_bytes(PrivateVisitor)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
mod block;
|
||||||
|
pub use crate::block::Block;
|
||||||
|
mod blockchain;
|
||||||
|
pub use crate::blockchain::Blockchain;
|
||||||
|
pub mod transaction;
|
||||||
|
pub use crate::transaction::Transaction;
|
||||||
|
pub mod utils;
|
||||||
|
pub use crate::utils::*;
|
||||||
|
pub mod hash;
|
||||||
|
pub use crate::hash::Hash;
|
||||||
|
pub mod keys;
|
||||||
|
pub use crate::keys::Signature;
|
@ -0,0 +1,40 @@
|
|||||||
|
use Wyrd::{Blockchain, Block, Transaction, Signature, Hash};
|
||||||
|
use Wyrd::transaction::Action;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Wyrd DNS 0.1.0");
|
||||||
|
test_blockchain()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_blockchain() -> () {
|
||||||
|
let mut blockchain = Blockchain::new(42, 0);
|
||||||
|
println!("Blockchain with genesis block has been created");
|
||||||
|
let signature = Signature::from_file("default.key", "").unwrap();
|
||||||
|
|
||||||
|
// Creating transaction
|
||||||
|
let action = Action::new_domain("test.zz".to_owned(), &signature, vec!["AAAA IN 301:2925::1".to_owned()], vec![], 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(Hash::new(sign_hash));
|
||||||
|
|
||||||
|
// Creating a block with that signed transaction
|
||||||
|
let mut block = blockchain.new_block(transaction);
|
||||||
|
|
||||||
|
// Mining the nonce
|
||||||
|
block.mine();
|
||||||
|
|
||||||
|
// Our block is ready, we can print it and add to Blockchain
|
||||||
|
let s = serde_json::to_string(&block).unwrap();
|
||||||
|
println!("Serialized block:\n{}", s);
|
||||||
|
blockchain.add_block(block);
|
||||||
|
println!("Second block added");
|
||||||
|
|
||||||
|
// Let's check if the blockchain is valid
|
||||||
|
if blockchain.check() {
|
||||||
|
println!("Blockchain is correct");
|
||||||
|
} else {
|
||||||
|
println!("Blockchain is corrupted, aborting");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
/// 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,124 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::transaction::Action::{MoveDomain, RenewDomain, ChangeDomain, NewDomain};
|
||||||
|
use crate::keys::*;
|
||||||
|
extern crate serde;
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
|
use serde::{Serialize, Deserialize, Serializer};
|
||||||
|
use serde::ser::SerializeStruct;
|
||||||
|
use std::fmt;
|
||||||
|
use crypto::util::fixed_time_eq;
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum Action {
|
||||||
|
NewDomain { name: String, owner: KeyPublic, #[serde(skip_serializing_if = "Vec::is_empty")] records: Vec<String>, #[serde(skip_serializing_if = "Vec::is_empty")] tags: Vec<String>, days: u16 },
|
||||||
|
ChangeDomain { name: String, records: Vec<String>, tags: Vec<String> },
|
||||||
|
RenewDomain { name: String, days: u16 },
|
||||||
|
MoveDomain { name: String, new_owner: KeyPublic },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Action {
|
||||||
|
pub fn new_domain(name: String, signature: &Signature, records: Vec<String>, tags: Vec<String>, days: u16) -> Self {
|
||||||
|
NewDomain {name, owner: signature.get_public(), records, tags, days}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_domain(name: String, records: Vec<String>, tags: Vec<String>) -> Self {
|
||||||
|
ChangeDomain {name, records, tags}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn renew_domain(name: String, days: u16) -> Self {
|
||||||
|
RenewDomain {name, days}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_domain(name: String, new_owner: [u8; 32]) -> Self {
|
||||||
|
MoveDomain {name, new_owner: KeyPublic::new(&new_owner)}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_bytes(&self) -> Vec<u8> {
|
||||||
|
// Let it panic if something is not okay
|
||||||
|
serde_json::to_vec(&self).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes(bytes: Vec<u8>) -> Self {
|
||||||
|
// Let it panic (for now) if something is not okay
|
||||||
|
serde_json::from_slice(bytes.as_slice()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Action {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Action::NewDomain { name, owner, records, tags, days } => {
|
||||||
|
fmt.debug_struct("NewDomain")
|
||||||
|
.field("name", name)
|
||||||
|
.field("owner", &owner)
|
||||||
|
.field("records", records)
|
||||||
|
.field("tags", tags)
|
||||||
|
.field("days", days)
|
||||||
|
.finish()
|
||||||
|
},
|
||||||
|
Action::ChangeDomain { name, records, tags } => {
|
||||||
|
fmt.debug_struct("ChangeDomain")
|
||||||
|
.field("name", name)
|
||||||
|
.field("records", records)
|
||||||
|
.field("tags", tags)
|
||||||
|
.finish()
|
||||||
|
},
|
||||||
|
Action::RenewDomain { name, days } => {
|
||||||
|
fmt.debug_struct("RenewDomain")
|
||||||
|
.field("name", name)
|
||||||
|
.field("days", days)
|
||||||
|
.finish()
|
||||||
|
},
|
||||||
|
Action::MoveDomain { name, new_owner } => {
|
||||||
|
fmt.debug_struct("MoveDomain")
|
||||||
|
.field("name", name)
|
||||||
|
.field("new_owner", new_owner)
|
||||||
|
.finish()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize, PartialEq)]
|
||||||
|
pub struct Transaction {
|
||||||
|
pub action: Action,
|
||||||
|
pub pub_key: KeyPublic,
|
||||||
|
pub signature: Hash,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transaction {
|
||||||
|
pub fn new(action: Action, pub_key: KeyPublic) -> Self {
|
||||||
|
Transaction {action, pub_key, signature: Hash::new([0u8; 64])}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_signature(&mut self, hash: Hash) {
|
||||||
|
self.signature = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_bytes(&self) -> Vec<u8> {
|
||||||
|
// Let it panic if something is not okay
|
||||||
|
serde_json::to_vec(&self).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Transaction {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt.debug_struct("Transaction")
|
||||||
|
.field("pub", &&self.pub_key)
|
||||||
|
.field("sign", &&self.signature)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Transaction {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where
|
||||||
|
S: Serializer {
|
||||||
|
let mut structure = serializer.serialize_struct("Transaction", 3).unwrap();
|
||||||
|
structure.serialize_field("action", &self.action);
|
||||||
|
structure.serialize_field("pub_key", &self.pub_key);
|
||||||
|
structure.serialize_field("signature", &self.signature);
|
||||||
|
structure.end()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
/// Convert bytes array to HEX format
|
||||||
|
pub fn to_hex(buf: &[u8]) -> String {
|
||||||
|
let mut result = String::new();
|
||||||
|
for x in buf.iter() {
|
||||||
|
result.push_str(&format!("{:01$X}", x, 2));
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// There is no default PartialEq implementation for arrays > 32 in size
|
||||||
|
pub fn same_hash(left: &[u8], right: &[u8]) -> bool {
|
||||||
|
for (x, y) in left.iter().zip(right) {
|
||||||
|
if x != y {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
Loading…
Reference in New Issue