Changed chain id to chain name (it will be a string). Implemented saving blockchain to sqlite DB.
This commit is contained in:
@@ -21,6 +21,7 @@ groestl = "0.8.0"
|
|||||||
base64 = "0.11.0"
|
base64 = "0.11.0"
|
||||||
chrono = "0.4.9"
|
chrono = "0.4.9"
|
||||||
rand = "0.7.2"
|
rand = "0.7.2"
|
||||||
|
sqlite = "0.25.3"
|
||||||
eventbus = "0.5.1"
|
eventbus = "0.5.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"chain_id": 42,
|
"chain_name": "test",
|
||||||
"version": 0,
|
"version_flags": 0,
|
||||||
"key_file": "default.key"
|
"key_file": "default.key"
|
||||||
}
|
}
|
||||||
+20
-5
@@ -17,8 +17,8 @@ use crate::keys::Bytes;
|
|||||||
pub struct Block {
|
pub struct Block {
|
||||||
pub index: u64,
|
pub index: u64,
|
||||||
pub timestamp: i64,
|
pub timestamp: i64,
|
||||||
pub chain_id: u32,
|
pub chain_name: String,
|
||||||
pub version: u32,
|
pub version_flags: u32,
|
||||||
pub difficulty: usize,
|
pub difficulty: usize,
|
||||||
pub random: u32,
|
pub random: u32,
|
||||||
pub nonce: u64,
|
pub nonce: u64,
|
||||||
@@ -31,12 +31,12 @@ pub struct Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Block {
|
impl Block {
|
||||||
pub fn new(index: u64, timestamp: i64, chain_id: u32, version: u32, prev_block_hash: Bytes, transaction: Option<Transaction>) -> Self {
|
pub fn new(index: u64, timestamp: i64, chain_name: &str, version_flags: u32, prev_block_hash: Bytes, transaction: Option<Transaction>) -> Self {
|
||||||
Block {
|
Block {
|
||||||
index,
|
index,
|
||||||
timestamp,
|
timestamp,
|
||||||
chain_id,
|
chain_name: chain_name.to_owned(),
|
||||||
version,
|
version_flags,
|
||||||
// TODO make difficulty parameter
|
// TODO make difficulty parameter
|
||||||
difficulty: 20,
|
difficulty: 20,
|
||||||
random: 0,
|
random: 0,
|
||||||
@@ -47,6 +47,21 @@ 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 {
|
||||||
|
Block {
|
||||||
|
index,
|
||||||
|
timestamp,
|
||||||
|
chain_name: chain_name.to_owned(),
|
||||||
|
version_flags,
|
||||||
|
difficulty,
|
||||||
|
random,
|
||||||
|
nonce,
|
||||||
|
transaction,
|
||||||
|
prev_block_hash,
|
||||||
|
hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn hash(data: &[u8]) -> Bytes {
|
pub fn hash(data: &[u8]) -> Bytes {
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
let mut digest = Sha256::new();
|
let mut digest = Sha256::new();
|
||||||
|
|||||||
+93
-15
@@ -1,46 +1,124 @@
|
|||||||
use crate::{Block, Transaction, Bytes};
|
use crate::{Block, Transaction, Bytes};
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use sqlite::{Connection, State, Readable, Statement, Error};
|
||||||
|
|
||||||
|
const DB_NAME: &str = "blockchain.db";
|
||||||
|
|
||||||
pub struct Blockchain {
|
pub struct Blockchain {
|
||||||
pub chain_id: u32,
|
pub chain_name: String,
|
||||||
pub version: u32,
|
pub version_flags: u32,
|
||||||
pub blocks: Vec<Block>,
|
pub blocks: Vec<Block>,
|
||||||
|
last_block: Option<Block>,
|
||||||
|
db: Connection,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Blockchain {
|
impl Blockchain {
|
||||||
pub fn new(chain_id: u32, version: u32) -> Self {
|
pub fn new(chain_name: &str, version_flags: u32) -> Self {
|
||||||
let mut blockchain = Blockchain{chain_id, version, blocks: Vec::new()};
|
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};
|
||||||
|
blockchain.init_db();
|
||||||
blockchain
|
blockchain
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_block(&self, transaction: Transaction) -> Block {
|
/// Reads options from DB or initializes and writes them to DB if not found
|
||||||
let prev_block = self.blocks.last().unwrap();
|
fn init_db(&mut self) {
|
||||||
let block = Block::new(prev_block.index + 1,Utc::now().timestamp(), self.chain_id, self.version, prev_block.hash.clone(), Some(transaction));
|
match self.db.prepare("SELECT * FROM blocks ORDER BY id DESC LIMIT 1;") {
|
||||||
block
|
Ok(mut statement) => {
|
||||||
|
while statement.next().unwrap() == State::Row {
|
||||||
|
match Self::get_block(&mut statement) {
|
||||||
|
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.last_block = Some(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("Loaded from DB: chain_name = {}, version_flags = {}", self.chain_name, self.version_flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
println!("No blockchain database found. Creating new.");
|
||||||
|
self.db.execute("
|
||||||
|
CREATE TABLE blocks (
|
||||||
|
'id' BIGINT,
|
||||||
|
'timestamp' BIGINT,
|
||||||
|
'chain_name' TEXT,
|
||||||
|
'version_flags' TEXT,
|
||||||
|
'difficulty' INTEGER,
|
||||||
|
'random' INTEGER,
|
||||||
|
'nonce' INTEGER,
|
||||||
|
'transaction' TEXT,
|
||||||
|
'prev_block_hash' BINARY,
|
||||||
|
'hash' BINARY
|
||||||
|
);
|
||||||
|
CREATE INDEX block_index ON blocks (id);"
|
||||||
|
).expect("Error creating blocks table");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_block(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))
|
||||||
}
|
}
|
||||||
|
|
||||||
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, &self.last_block) {
|
||||||
println!("Adding block:\n{:?}", &block);
|
println!("Adding block:\n{:?}", &block);
|
||||||
self.blocks.push(block);
|
self.blocks.push(block.clone());
|
||||||
|
self.last_block = Some(block.clone());
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
statement.bind(1, block.index as i64);
|
||||||
|
statement.bind(2, block.timestamp as i64);
|
||||||
|
statement.bind(3, &*block.chain_name);
|
||||||
|
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);
|
||||||
|
match block.transaction {
|
||||||
|
None => { statement.bind(8, ""); }
|
||||||
|
Some(transaction) => { statement.bind(8, &*transaction.to_string()); }
|
||||||
|
}
|
||||||
|
statement.bind(9, block.prev_block_hash.as_bytes());
|
||||||
|
statement.bind(10, block.hash.as_bytes());
|
||||||
|
statement.next().expect("Error adding block to DB");
|
||||||
} else {
|
} else {
|
||||||
println!("Bad block found, ignoring:\n{:?}", &block);
|
println!("Bad block found, ignoring:\n{:?}", &block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(&self) -> bool {
|
pub fn get_last_block(&self) -> Option<Block> {
|
||||||
|
self.last_block.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*pub fn check(&self) -> bool {
|
||||||
let mut prev_block = None;
|
let mut prev_block = None;
|
||||||
for block in self.blocks.iter() {
|
for block in self.blocks.iter() {
|
||||||
if !self.check_block(block, prev_block) {
|
if !self.check_block(block, &prev_block) {
|
||||||
println!("Block {:?} is bad", block);
|
println!("Block {:?} is bad", block);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
prev_block = Some(block);
|
prev_block = Some(block);
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}*/
|
||||||
|
|
||||||
fn check_block(&self, block: &Block, prev_block: Option<&Block>) -> bool {
|
fn check_block(&self, block: &Block, prev_block: &Option<Block>) -> bool {
|
||||||
if !Self::check_block_hash(block) {
|
if !Self::check_block_hash(block) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -48,7 +126,7 @@ impl Blockchain {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return block.prev_block_hash == prev_block.unwrap().hash;
|
return block.prev_block_hash == prev_block.as_ref().unwrap().hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_block_hash(block: &Block) -> bool {
|
pub fn check_block_hash(block: &Block) -> bool {
|
||||||
|
|||||||
+2
-2
@@ -46,8 +46,8 @@ impl Context {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub chain_id: u32,
|
pub chain_name: String,
|
||||||
pub version: u32,
|
pub version_flags: u32,
|
||||||
pub key_file: String
|
pub key_file: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-39
@@ -27,7 +27,7 @@ fn main() {
|
|||||||
None => { generate_key(KEYSTORE_DIFFICULTY, Arc::new(AtomicBool::new(true))).expect("Could not load or generate keypair") }
|
None => { generate_key(KEYSTORE_DIFFICULTY, Arc::new(AtomicBool::new(true))).expect("Could not load or generate keypair") }
|
||||||
Some(keystore) => { keystore }
|
Some(keystore) => { keystore }
|
||||||
};
|
};
|
||||||
let blockchain: Blockchain = Blockchain::new(settings.chain_id, settings.version);
|
let blockchain: Blockchain = Blockchain::new(&settings.chain_name, settings.version_flags);
|
||||||
let context: Arc<Mutex<Context>> = Arc::new(Mutex::new(Context::new(settings, keystore, blockchain)));
|
let context: Arc<Mutex<Context>> = Arc::new(Mutex::new(Context::new(settings, keystore, blockchain)));
|
||||||
|
|
||||||
let mut miner_obj = Miner::new(context.clone());
|
let mut miner_obj = Miner::new(context.clone());
|
||||||
@@ -40,19 +40,10 @@ fn main() {
|
|||||||
|
|
||||||
fn create_genesis_if_needed(context: &Arc<Mutex<Context>>, miner: &Arc<Mutex<Miner>>) {
|
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
|
// TODO check settings and if there is no mention of bootstrap nodes, generate genesis block
|
||||||
let keystore = {
|
|
||||||
// This code block makes it possible to contain quick lock here, and let the miner below work
|
|
||||||
let context_guard = context.lock().unwrap();
|
let context_guard = context.lock().unwrap();
|
||||||
if context_guard.get_blockchain().blocks.is_empty() {
|
if context_guard.get_blockchain().get_last_block().is_none() {
|
||||||
// If blockchain is empty, we are going to mine a Genesis block
|
// If blockchain is empty, we are going to mine a Genesis block
|
||||||
Some(context_guard.get_keystore())
|
create_genesis(miner.clone(), GENESIS_ZONE, &context_guard.get_keystore(), GENESIS_ZONE_DIFFICULTY);
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if keystore.is_some() {
|
|
||||||
create_genesis(miner.clone(), GENESIS_ZONE, &keystore.unwrap(), GENESIS_ZONE_DIFFICULTY);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,30 +183,3 @@ fn inline_style(s: &str) -> String {
|
|||||||
fn inline_script(s: &str) -> String {
|
fn inline_script(s: &str) -> String {
|
||||||
format!(r#"<script type="text/javascript">{}</script>"#, s)
|
format!(r#"<script type="text/javascript">{}</script>"#, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_blockchain() -> () {
|
|
||||||
let mut blockchain = Blockchain::new(42, 0);
|
|
||||||
println!("Blockchain with genesis block has been created");
|
|
||||||
let keystore = Keystore::from_file("default.key", "").unwrap();
|
|
||||||
|
|
||||||
/*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);
|
|
||||||
|
|
||||||
// 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 block2: Block = serde_json::from_str(&s).unwrap();
|
|
||||||
println!("DeSerialized block:\n{:?}", block2);*/
|
|
||||||
|
|
||||||
// Let's check if the blockchain is valid
|
|
||||||
if blockchain.check() {
|
|
||||||
println!("Blockchain is correct");
|
|
||||||
} else {
|
|
||||||
println!("Blockchain is corrupted, aborting");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+11
-16
@@ -11,8 +11,8 @@ use num_cpus;
|
|||||||
pub struct Miner {
|
pub struct Miner {
|
||||||
context: Arc<Mutex<Context>>,
|
context: Arc<Mutex<Context>>,
|
||||||
keystore: Keystore,
|
keystore: Keystore,
|
||||||
chain_id: u32,
|
chain_name: String,
|
||||||
version: u32,
|
version_flags: u32,
|
||||||
transactions: Arc<Mutex<Vec<Transaction>>>,
|
transactions: Arc<Mutex<Vec<Transaction>>>,
|
||||||
last_block: Option<Block>,
|
last_block: Option<Block>,
|
||||||
running: Arc<AtomicBool>,
|
running: Arc<AtomicBool>,
|
||||||
@@ -26,8 +26,8 @@ impl Miner {
|
|||||||
Miner {
|
Miner {
|
||||||
context: context.clone(),
|
context: context.clone(),
|
||||||
keystore: c.keystore.clone(),
|
keystore: c.keystore.clone(),
|
||||||
chain_id: c.settings.chain_id,
|
chain_name: c.settings.chain_name.clone(),
|
||||||
version: c.settings.version,
|
version_flags: c.settings.version_flags,
|
||||||
transactions: Arc::new(Mutex::new(Vec::new())),
|
transactions: Arc::new(Mutex::new(Vec::new())),
|
||||||
last_block: c.blockchain.blocks.last().cloned(),
|
last_block: c.blockchain.blocks.last().cloned(),
|
||||||
running: Arc::new(AtomicBool::new(false)),
|
running: Arc::new(AtomicBool::new(false)),
|
||||||
@@ -83,12 +83,12 @@ impl Miner {
|
|||||||
|
|
||||||
fn mine_internal(context: Arc<Mutex<Context>>, transactions: Arc<Mutex<Vec<Transaction>>>, mut transaction: Transaction, mining: Arc<AtomicBool>, cond_var: Arc<Condvar>) {
|
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 last_block_time = 0i64;
|
||||||
let mut chain_id = 0u32;
|
let mut chain_name= String::new();
|
||||||
let mut version = 0u32;
|
let mut version_flags= 0u32;
|
||||||
{
|
{
|
||||||
let c = context.lock().unwrap();
|
let c = context.lock().unwrap();
|
||||||
chain_id = c.settings.chain_id;
|
chain_name = c.settings.chain_name.clone();
|
||||||
version = c.settings.version;
|
version_flags = c.settings.version_flags;
|
||||||
}
|
}
|
||||||
let block = {
|
let block = {
|
||||||
if transaction.signature.is_zero() {
|
if transaction.signature.is_zero() {
|
||||||
@@ -99,17 +99,17 @@ impl Miner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get last block for mining
|
// Get last block for mining
|
||||||
let last_block = { context.lock().unwrap().blockchain.blocks.last().cloned() };
|
let last_block = { context.lock().unwrap().blockchain.get_last_block() };
|
||||||
match last_block {
|
match last_block {
|
||||||
None => {
|
None => {
|
||||||
println!("Mining genesis block");
|
println!("Mining genesis block");
|
||||||
// Creating a block with that signed transaction
|
// Creating a block with that signed transaction
|
||||||
Block::new(0, Utc::now().timestamp(), chain_id, version, Bytes::zero32(), Some(transaction.clone()))
|
Block::new(0, Utc::now().timestamp(), &chain_name, version_flags, Bytes::zero32(), Some(transaction.clone()))
|
||||||
},
|
},
|
||||||
Some(block) => {
|
Some(block) => {
|
||||||
last_block_time = block.timestamp;
|
last_block_time = block.timestamp;
|
||||||
// Creating a block with that signed transaction
|
// Creating a block with that signed transaction
|
||||||
Block::new(block.index + 1, Utc::now().timestamp(), chain_id, version, block.hash.clone(), Some(transaction.clone()))
|
Block::new(block.index + 1, Utc::now().timestamp(), &chain_name, version_flags, block.hash.clone(), Some(transaction.clone()))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -149,11 +149,6 @@ impl Miner {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_last_block(&self) -> Option<Block> {
|
|
||||||
let context = self.context.lock().unwrap();
|
|
||||||
context.blockchain.blocks.last().cloned()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_hash(digest: &mut dyn Digest, mut block: Block, prev_block_time: i64, running: Arc<AtomicBool>) -> Option<Block> {
|
fn find_hash(digest: &mut dyn Digest, mut block: Block, prev_block_time: i64, running: Arc<AtomicBool>) -> Option<Block> {
|
||||||
|
|||||||
@@ -31,6 +31,13 @@ impl Transaction {
|
|||||||
Transaction { identity, method, data, pub_key, signature: Bytes::zero64() }
|
Transaction { identity, method, data, pub_key, signature: Bytes::zero64() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_json(json: &str) -> Option<Self> {
|
||||||
|
match serde_json::from_str(json) {
|
||||||
|
Ok(transaction) => Some(transaction),
|
||||||
|
Err(_) => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_signature(&mut self, hash: Bytes) {
|
pub fn set_signature(&mut self, hash: Bytes) {
|
||||||
self.signature = hash;
|
self.signature = hash;
|
||||||
}
|
}
|
||||||
@@ -39,6 +46,11 @@ impl Transaction {
|
|||||||
// Let it panic if something is not okay
|
// Let it panic if something is not okay
|
||||||
serde_json::to_vec(&self).unwrap()
|
serde_json::to_vec(&self).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
// Let it panic if something is not okay
|
||||||
|
serde_json::to_string(&self).unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Transaction {
|
impl fmt::Debug for Transaction {
|
||||||
|
|||||||
Reference in New Issue
Block a user