mirror of
https://github.com/Revertron/Alfis
synced 2024-11-07 09:20:31 +00:00
Refactored block and transaction structure. Introduced a 'confirmation' entity to avoid block interceptions (at least make them pointless).
This commit is contained in:
parent
279b3e87c3
commit
1331f44b0e
@ -1,7 +1,8 @@
|
||||
{
|
||||
"chain_name": "test",
|
||||
"version_flags": 0,
|
||||
"key_file": "default.key",
|
||||
"origin": "",
|
||||
"version": 0,
|
||||
"key_file": "default3.key",
|
||||
"listen": "127.0.0.1:4244",
|
||||
"public": true,
|
||||
"peers": [
|
||||
|
@ -17,8 +17,7 @@ use crate::Transaction;
|
||||
pub struct Block {
|
||||
pub index: u64,
|
||||
pub timestamp: i64,
|
||||
pub chain_name: String,
|
||||
pub version_flags: u32,
|
||||
pub version: u32,
|
||||
pub difficulty: usize,
|
||||
pub random: u32,
|
||||
pub nonce: u64,
|
||||
@ -31,12 +30,11 @@ pub struct Block {
|
||||
}
|
||||
|
||||
impl Block {
|
||||
pub fn new(index: u64, timestamp: i64, chain_name: &str, version_flags: u32, prev_block_hash: Bytes, transaction: Option<Transaction>) -> Self {
|
||||
pub fn new(index: u64, timestamp: i64, version: u32, prev_block_hash: Bytes, transaction: Option<Transaction>) -> Self {
|
||||
Block {
|
||||
index,
|
||||
timestamp,
|
||||
chain_name: chain_name.to_owned(),
|
||||
version_flags,
|
||||
version,
|
||||
// TODO make difficulty parameter
|
||||
difficulty: 20,
|
||||
random: 0,
|
||||
@ -47,12 +45,11 @@ impl Block {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_all_params(index: u64, timestamp: i64, chain_name: &str, version_flags: u32, difficulty: usize, random: u32, nonce: u64, prev_block_hash: Bytes, hash: Bytes, transaction: Option<Transaction>) -> Self {
|
||||
pub fn from_all_params(index: u64, timestamp: i64, version: u32, difficulty: usize, random: u32, nonce: u64, prev_block_hash: Bytes, hash: Bytes, transaction: Option<Transaction>) -> Self {
|
||||
Block {
|
||||
index,
|
||||
timestamp,
|
||||
chain_name: chain_name.to_owned(),
|
||||
version_flags,
|
||||
version,
|
||||
difficulty,
|
||||
random,
|
||||
nonce,
|
||||
|
@ -6,17 +6,17 @@ use crate::{Block, Bytes, Keystore, Transaction};
|
||||
const DB_NAME: &str = "blockchain.db";
|
||||
|
||||
pub struct Blockchain {
|
||||
pub chain_name: String,
|
||||
pub version_flags: u32,
|
||||
origin: String,
|
||||
pub version: u32,
|
||||
pub blocks: Vec<Block>,
|
||||
last_block: Option<Block>,
|
||||
db: Connection,
|
||||
}
|
||||
|
||||
impl Blockchain {
|
||||
pub fn new(chain_name: &str, version_flags: u32) -> Self {
|
||||
pub fn new(origin: String, version: u32) -> Self {
|
||||
let db = sqlite::open(DB_NAME).expect("Unable to open blockchain DB");
|
||||
let mut blockchain = Blockchain{ chain_name: chain_name.to_owned(), version_flags, blocks: Vec::new(), last_block: None, db};
|
||||
let mut blockchain = Blockchain{ origin, version, blocks: Vec::new(), last_block: None, db};
|
||||
blockchain.init_db();
|
||||
blockchain
|
||||
}
|
||||
@ -30,12 +30,11 @@ impl Blockchain {
|
||||
None => { println!("Something wrong with block in DB!"); }
|
||||
Some(block) => {
|
||||
println!("Loaded last block: {:?}", &block);
|
||||
self.chain_name = block.chain_name.clone();
|
||||
self.version_flags = block.version_flags;
|
||||
self.version = block.version;
|
||||
self.last_block = Some(block);
|
||||
}
|
||||
}
|
||||
println!("Loaded from DB: chain_name = {}, version_flags = {}", self.chain_name, self.version_flags);
|
||||
println!("Blockchain version from DB = {}", self.version);
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
@ -44,8 +43,7 @@ impl Blockchain {
|
||||
CREATE TABLE blocks (
|
||||
'id' BIGINT,
|
||||
'timestamp' BIGINT,
|
||||
'chain_name' TEXT,
|
||||
'version_flags' TEXT,
|
||||
'version' TEXT,
|
||||
'difficulty' INTEGER,
|
||||
'random' INTEGER,
|
||||
'nonce' INTEGER,
|
||||
@ -54,7 +52,7 @@ impl Blockchain {
|
||||
'hash' BINARY
|
||||
);
|
||||
CREATE INDEX block_index ON blocks (id);
|
||||
CREATE TABLE transactions (id INTEGER PRIMARY KEY AUTOINCREMENT, identity BINARY, method TEXT, data TEXT, pub_key BINARY, signature BINARY);
|
||||
CREATE TABLE transactions (id INTEGER PRIMARY KEY AUTOINCREMENT, identity BINARY, confirmation BINARY, method TEXT, data TEXT, pub_key BINARY, signature BINARY);
|
||||
CREATE INDEX ids ON transactions (identity);"
|
||||
).expect("Error creating blocks table");
|
||||
}
|
||||
@ -71,24 +69,23 @@ impl Blockchain {
|
||||
{
|
||||
// Adding block to DB
|
||||
let mut statement = self.db.prepare("INSERT INTO blocks (\
|
||||
id, timestamp, chain_name, version_flags, difficulty,\
|
||||
random, nonce, 'transaction', prev_block_hash, hash)\
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);").unwrap();
|
||||
id, timestamp, version, difficulty, random,\
|
||||
nonce, 'transaction', prev_block_hash, hash)\
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);").unwrap();
|
||||
statement.bind(1, block.index as i64);
|
||||
statement.bind(2, block.timestamp as i64);
|
||||
statement.bind(3, block.chain_name.as_ref() as &str);
|
||||
statement.bind(4, block.version_flags as i64);
|
||||
statement.bind(5, block.difficulty as i64);
|
||||
statement.bind(6, block.random as i64);
|
||||
statement.bind(7, block.nonce as i64);
|
||||
statement.bind(3, block.version as i64);
|
||||
statement.bind(4, block.difficulty as i64);
|
||||
statement.bind(5, block.random as i64);
|
||||
statement.bind(6, block.nonce as i64);
|
||||
match &transaction {
|
||||
None => { statement.bind(8, ""); }
|
||||
None => { statement.bind(7, ""); }
|
||||
Some(transaction) => {
|
||||
statement.bind(8, transaction.to_string().as_ref() as &str);
|
||||
statement.bind(7, transaction.to_string().as_ref() as &str);
|
||||
}
|
||||
}
|
||||
statement.bind(9, block.prev_block_hash.as_bytes());
|
||||
statement.bind(10, block.hash.as_bytes());
|
||||
statement.bind(8, block.prev_block_hash.as_bytes());
|
||||
statement.bind(9, block.hash.as_bytes());
|
||||
statement.next().expect("Error adding block to DB");
|
||||
}
|
||||
|
||||
@ -104,12 +101,13 @@ impl Blockchain {
|
||||
}
|
||||
|
||||
fn add_transaction(&mut self, t: &Transaction) {
|
||||
let mut statement = self.db.prepare("INSERT INTO transactions (identity, method, data, pub_key, signature) VALUES (?, ?, ?, ?, ?)").unwrap();
|
||||
let mut statement = self.db.prepare("INSERT INTO transactions (identity, confirmation, method, data, pub_key, signature) VALUES (?, ?, ?, ?, ?, ?)").unwrap();
|
||||
statement.bind(1, t.identity.as_bytes());
|
||||
statement.bind(2, t.method.as_ref() as &str);
|
||||
statement.bind(3, t.data.as_ref() as &str);
|
||||
statement.bind(4, t.pub_key.as_bytes());
|
||||
statement.bind(5, t.signature.as_bytes());
|
||||
statement.bind(2, t.confirmation.as_bytes());
|
||||
statement.bind(3, t.method.as_ref() as &str);
|
||||
statement.bind(4, t.data.as_ref() as &str);
|
||||
statement.bind(5, t.pub_key.as_bytes());
|
||||
statement.bind(6, t.signature.as_bytes());
|
||||
statement.next().expect("Error adding transaction to DB");
|
||||
}
|
||||
|
||||
@ -212,15 +210,14 @@ impl Blockchain {
|
||||
fn get_block_from_statement(statement: &mut Statement) -> Option<Block> {
|
||||
let index = statement.read::<i64>(0).unwrap() as u64;
|
||||
let timestamp = statement.read::<i64>(1).unwrap();
|
||||
let chain_name = statement.read::<String>(2).unwrap();
|
||||
let version_flags = statement.read::<i64>(3).unwrap() as u32;
|
||||
let difficulty = statement.read::<i64>(4).unwrap() as usize;
|
||||
let random = statement.read::<i64>(5).unwrap() as u32;
|
||||
let nonce = statement.read::<i64>(6).unwrap() as u64;
|
||||
let transaction = Transaction::from_json(&statement.read::<String>(7).unwrap());
|
||||
let prev_block_hash = Bytes::from_bytes(statement.read::<Vec<u8>>(8).unwrap().as_slice());
|
||||
let hash = Bytes::from_bytes(statement.read::<Vec<u8>>(9).unwrap().as_slice());
|
||||
Some(Block::from_all_params(index, timestamp, &chain_name, version_flags, difficulty, random, nonce, prev_block_hash, hash, transaction))
|
||||
let version = statement.read::<i64>(2).unwrap() as u32;
|
||||
let difficulty = statement.read::<i64>(3).unwrap() as usize;
|
||||
let random = statement.read::<i64>(4).unwrap() as u32;
|
||||
let nonce = statement.read::<i64>(5).unwrap() as u64;
|
||||
let transaction = Transaction::from_json(&statement.read::<String>(6).unwrap());
|
||||
let prev_block_hash = Bytes::from_bytes(statement.read::<Vec<u8>>(7).unwrap().as_slice());
|
||||
let hash = Bytes::from_bytes(statement.read::<Vec<u8>>(8).unwrap().as_slice());
|
||||
Some(Block::from_all_params(index, timestamp, version, difficulty, random, nonce, prev_block_hash, hash, transaction))
|
||||
}
|
||||
|
||||
pub fn check_block_hash(block: &Block) -> bool {
|
||||
|
@ -12,6 +12,7 @@ use crypto::digest::Digest;
|
||||
#[derive(Clone, Deserialize, PartialEq)]
|
||||
pub struct Transaction {
|
||||
pub identity: Bytes,
|
||||
pub confirmation: Bytes,
|
||||
pub method: String,
|
||||
pub data: String,
|
||||
pub pub_key: Bytes,
|
||||
@ -20,12 +21,13 @@ pub struct Transaction {
|
||||
|
||||
impl Transaction {
|
||||
pub fn from_str(identity: String, method: String, data: String, pub_key: Bytes) -> Self {
|
||||
let bytes = Self::hash_identity(&identity);
|
||||
return Self::new(bytes, method, data, pub_key);
|
||||
let hash = Self::hash_identity(&identity);
|
||||
let confirmation = Self::hash_with_key(&identity, &pub_key);
|
||||
return Self::new(hash, confirmation, method, data, pub_key);
|
||||
}
|
||||
|
||||
pub fn new(identity: Bytes, method: String, data: String, pub_key: Bytes) -> Self {
|
||||
Transaction { identity, method, data, pub_key, signature: Bytes::zero64() }
|
||||
pub fn new(identity: Bytes, confirmation: Bytes, method: String, data: String, pub_key: Bytes) -> Self {
|
||||
Transaction { identity, confirmation, method, data, pub_key, signature: Bytes::zero64() }
|
||||
}
|
||||
|
||||
pub fn from_json(json: &str) -> Option<Self> {
|
||||
@ -56,12 +58,22 @@ impl Transaction {
|
||||
digest.result(&mut buf);
|
||||
Bytes::from_bytes(&buf)
|
||||
}
|
||||
|
||||
pub fn hash_with_key(identity: &str, key: &Bytes) -> Bytes {
|
||||
let mut buf: [u8; 32] = [0; 32];
|
||||
let mut digest = Sha256::new();
|
||||
digest.input_str(identity);
|
||||
digest.input(key.as_bytes());
|
||||
digest.result(&mut buf);
|
||||
Bytes::from_bytes(&buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Transaction {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Transaction")
|
||||
.field("identity", &self.identity)
|
||||
.field("confirmation", &self.confirmation)
|
||||
.field("method", &self.method)
|
||||
.field("data", &self.data)
|
||||
.field("pub", &&self.pub_key)
|
||||
@ -71,10 +83,10 @@ impl fmt::Debug for Transaction {
|
||||
}
|
||||
|
||||
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();
|
||||
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", 6).unwrap();
|
||||
structure.serialize_field("identity", &self.identity);
|
||||
structure.serialize_field("confirmation", &self.confirmation);
|
||||
structure.serialize_field("method", &self.method);
|
||||
structure.serialize_field("data", &self.data);
|
||||
structure.serialize_field("pub_key", &self.pub_key);
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{Keystore, Blockchain, Bus};
|
||||
use crate::{Keystore, Blockchain, Bus, Bytes};
|
||||
use crate::event::Event;
|
||||
use std::collections::HashMap;
|
||||
use serde::{Serialize, Deserialize, Serializer, Deserializer};
|
||||
@ -7,9 +7,9 @@ use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
pub struct Context {
|
||||
pub(crate) settings: Settings,
|
||||
pub(crate) keystore: Keystore,
|
||||
pub(crate) blockchain: Blockchain,
|
||||
pub settings: Settings,
|
||||
pub keystore: Keystore,
|
||||
pub blockchain: Blockchain,
|
||||
pub bus: Bus<Event>,
|
||||
}
|
||||
|
||||
@ -48,8 +48,8 @@ impl Context {
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Settings {
|
||||
pub chain_name: String,
|
||||
pub version_flags: u32,
|
||||
pub origin: String,
|
||||
pub version: u32,
|
||||
pub key_file: String,
|
||||
pub listen: String,
|
||||
pub public: bool,
|
||||
@ -76,4 +76,9 @@ impl Settings {
|
||||
Err(..) => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_origin(&self) -> Bytes {
|
||||
let origin = crate::from_hex(&self.origin).expect("Wrong origin in settings");
|
||||
Bytes::from_bytes(origin.as_slice())
|
||||
}
|
||||
}
|
13
src/main.rs
13
src/main.rs
@ -31,7 +31,7 @@ fn main() {
|
||||
None => { generate_key(KEYSTORE_DIFFICULTY, Arc::new(AtomicBool::new(true))).expect("Could not load or generate keypair") }
|
||||
Some(keystore) => { keystore }
|
||||
};
|
||||
let blockchain: Blockchain = Blockchain::new(&settings.chain_name, settings.version_flags);
|
||||
let blockchain: Blockchain = Blockchain::new(settings.origin.clone(), settings.version);
|
||||
let context: Arc<Mutex<Context>> = Arc::new(Mutex::new(Context::new(settings, keystore, blockchain)));
|
||||
|
||||
let mut miner_obj = Miner::new(context.clone());
|
||||
@ -46,11 +46,14 @@ fn main() {
|
||||
}
|
||||
|
||||
fn create_genesis_if_needed(context: &Arc<Mutex<Context>>, miner: &Arc<Mutex<Miner>>) {
|
||||
// TODO check settings and if there is no mention of bootstrap nodes, generate genesis block
|
||||
let context_guard = context.lock().unwrap();
|
||||
if context_guard.get_blockchain().last_block().is_none() {
|
||||
// If there is no origin in settings and no blockchain in DB, generate genesis block
|
||||
let context = context.lock().unwrap();
|
||||
// TODO compare first block's hash to origin
|
||||
let last_block = context.get_blockchain().last_block();
|
||||
let origin = context.settings.origin.clone();
|
||||
if origin.eq("") && last_block.is_none() {
|
||||
// If blockchain is empty, we are going to mine a Genesis block
|
||||
create_genesis(miner.clone(), GENESIS_ZONE, &context_guard.get_keystore(), GENESIS_ZONE_DIFFICULTY);
|
||||
create_genesis(miner.clone(), GENESIS_ZONE, &context.get_keystore(), GENESIS_ZONE_DIFFICULTY);
|
||||
}
|
||||
}
|
||||
|
||||
|
16
src/miner.rs
16
src/miner.rs
@ -14,8 +14,7 @@ use crate::event::Event;
|
||||
pub struct Miner {
|
||||
context: Arc<Mutex<Context>>,
|
||||
keystore: Keystore,
|
||||
chain_name: String,
|
||||
version_flags: u32,
|
||||
version: u32,
|
||||
transactions: Arc<Mutex<Vec<Transaction>>>,
|
||||
last_block: Option<Block>,
|
||||
running: Arc<AtomicBool>,
|
||||
@ -29,8 +28,7 @@ impl Miner {
|
||||
Miner {
|
||||
context: context.clone(),
|
||||
keystore: c.keystore.clone(),
|
||||
chain_name: c.settings.chain_name.clone(),
|
||||
version_flags: c.settings.version_flags,
|
||||
version: c.settings.version,
|
||||
transactions: Arc::new(Mutex::new(Vec::new())),
|
||||
last_block: c.blockchain.blocks.last().cloned(),
|
||||
running: Arc::new(AtomicBool::new(false)),
|
||||
@ -93,13 +91,11 @@ impl Miner {
|
||||
|
||||
fn mine_internal(context: Arc<Mutex<Context>>, transactions: Arc<Mutex<Vec<Transaction>>>, mut transaction: Transaction, mining: Arc<AtomicBool>, cond_var: Arc<Condvar>) {
|
||||
let mut last_block_time = 0i64;
|
||||
let mut chain_name= String::new();
|
||||
let mut version_flags= 0u32;
|
||||
let mut version= 0u32;
|
||||
{
|
||||
let mut c = context.lock().unwrap();
|
||||
c.bus.post(Event::MinerStarted);
|
||||
chain_name = c.settings.chain_name.clone();
|
||||
version_flags = c.settings.version_flags;
|
||||
version = c.settings.version;
|
||||
}
|
||||
let block = {
|
||||
if transaction.signature.is_zero() {
|
||||
@ -115,12 +111,12 @@ impl Miner {
|
||||
None => {
|
||||
println!("Mining genesis block");
|
||||
// Creating a block with that signed transaction
|
||||
Block::new(0, Utc::now().timestamp(), &chain_name, version_flags, Bytes::zero32(), Some(transaction.clone()))
|
||||
Block::new(0, Utc::now().timestamp(), version, Bytes::zero32(), Some(transaction.clone()))
|
||||
},
|
||||
Some(block) => {
|
||||
last_block_time = block.timestamp;
|
||||
// Creating a block with that signed transaction
|
||||
Block::new(block.index + 1, Utc::now().timestamp(), &chain_name, version_flags, block.hash.clone(), Some(transaction.clone()))
|
||||
Block::new(block.index + 1, Utc::now().timestamp(), version, block.hash.clone(), Some(transaction.clone()))
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -7,8 +7,8 @@ use crate::p2p::peer::Peer;
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum Message {
|
||||
Error,
|
||||
Hand { chain: String, version: u32, public: bool },
|
||||
Shake { ok: bool, height: u64 },
|
||||
Hand { origin: String, version: u32, public: bool },
|
||||
Shake { origin: String, version: u32, ok: bool, height: u64 },
|
||||
Ping { height: u64 },
|
||||
Pong { height: u64 },
|
||||
GetPeers,
|
||||
@ -26,12 +26,12 @@ impl Message {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hand(chain: &str, version: u32, public: bool) -> Self {
|
||||
Message::Hand { chain: chain.to_owned(), version, public }
|
||||
pub fn hand(origin: &str, version: u32, public: bool) -> Self {
|
||||
Message::Hand { origin: origin.to_owned(), version, public }
|
||||
}
|
||||
|
||||
pub fn shake(ok: bool, height: u64) -> Self {
|
||||
Message::Shake { ok, height }
|
||||
pub fn shake(origin: &str, version: u32, ok: bool, height: u64) -> Self {
|
||||
Message::Shake { origin: origin.to_owned(), version, ok, height }
|
||||
}
|
||||
|
||||
pub fn ping(height: u64) -> Self {
|
||||
|
@ -176,7 +176,7 @@ fn handle_connection_event(context: Arc<Mutex<Context>>, peers: &mut Peers, regi
|
||||
println!("Sending hello to socket {}", event.token().0);
|
||||
let data: String = {
|
||||
let mut c = context.lock().unwrap();
|
||||
let message = Message::hand(&c.settings.chain_name, c.settings.version_flags, c.settings.public);
|
||||
let message = Message::hand(&c.settings.origin, c.settings.version, c.settings.public);
|
||||
serde_json::to_string(&message).unwrap()
|
||||
};
|
||||
send_message(peer.get_stream(), &data.into_bytes());
|
||||
@ -255,17 +255,18 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
|
||||
context.blockchain.height()
|
||||
};
|
||||
match message {
|
||||
Message::Hand { chain, version, public } => {
|
||||
Message::Hand { origin: origin, version, public } => {
|
||||
let context = context.lock().unwrap();
|
||||
if chain == context.settings.chain_name && version == context.settings.version_flags {
|
||||
if origin == context.settings.origin && version == context.settings.version {
|
||||
let mut peer = peers.get_mut_peer(token).unwrap();
|
||||
peer.set_public(public);
|
||||
State::message(Message::shake(true, context.blockchain.height()))
|
||||
State::message(Message::shake(&context.settings.origin, context.settings.version, true, context.blockchain.height()))
|
||||
} else {
|
||||
State::Error
|
||||
}
|
||||
}
|
||||
Message::Shake { ok, height } => {
|
||||
Message::Shake { origin, version, ok, height } => {
|
||||
// TODO check origin and version for compatibility
|
||||
if ok {
|
||||
if height > my_height {
|
||||
State::message(Message::GetBlock { index: my_height + 1u64 })
|
||||
|
Loading…
Reference in New Issue
Block a user