A simple structure of a blockchain can be understood as countless blocks connected together like a chain to form a blockchain.
The green part is called the block header, which includes (pre hash, tx hash, time).
The black part and the blue part are called the block body, which includes (hash, transaction).
The pre hash is the hash of the previous block.
Time represents the transaction time, a timestamp.
Tx hash is used to ensure that the data is not tampered with. The data of each block can theoretically be tampered with, but if it is changed, the hash will not match.
Transaction refers to transaction information.
Finally, there is the hash value of the entire block, which is equivalent to the identifier of each block. Similarly, as long as a data in the block is changed, the hash value will change.
Code it!!!
First, we need to use the following packages, open the terminal
cargo add serde
cargo add bincode
cargo add rust-crypto
cargo add chrono
The serde package is used for serialization and deserialization, which is a very common function in network transmission and data storage. Serialization and deserialization can be generally explained as follows: serialization: converting objects into a format that is easy to transmit, common serialization formats include binary format, byte array, JSON string, XML string. Deserialization: the process of restoring serialized data to objects.
The bincode package is a binary encoding format.
The crypto package is used for hashing.
The chrono package is used for timestamps.
Add the following code at the top:
use bincode;
use serde::{Deserialize, Serialize};
use crypto::digest::Digest;
use crypto::sha3::Sha3;
use chrono::prelude::*;
Define the block header:
struct BlockHeader {
time: i64,
tx_hash: String,
pre_hash: String,
}
Define the block:
struct Block {
header: BlockHeader,
hash: String,
data: String, //transactions data
}
Use the packages we just added to write two methods for serialization and deserialization:
// Serialization
fn my_serialize<T: ?Sized>(value: &T) -> Vec<u8>
where T: Serialize,
{
let serialized = bincode::serialize(value).unwrap();
serialized
}
// Deserialization
fn my_deserialize<'a, T>(bytes: &'a[u8]) -> T
where T: Deserialize<'a>,
{
let deserialized = bincode::deserialize(bytes).unwrap();
deserialized
}
Use the rust-crypto package to calculate the hash:
fn get_hash(value: &[u8]) -> String {
let mut hasher = Sha3::sha3_256();
hasher.input(value);
hasher.result_str()
}
Implement these methods for the Block defined earlier:
impl Block {
fn set_hash(&mut self) {
let header = coder::my_serialize(&(self.header));
self.hash = coder::get_hash(&header[..]);
}
fn new_block(data: String, pre_hash: String) -> Block {
let transactions = coder::my_serialize(&data);
let tx_hash = coder::get_hash(&transactions[..]);
let time = Utc::now().timestamp();
let mut block = Block {
header: BlockHeader {
time: time,
tx_hash: tx_hash, //transactions data merkle root hash
pre_hash: pre_hash,
},
hash: "".to_string(),
data: data,
};
block.set_hash();
block
}
}
Define the blockchain:
struct BlockChain {
blocks: Vec<block::Block>,
}
impl BlockChain {
fn add_block(&mut self, data: String) {
let pre_block = &self.blocks[self.blocks.len() - 1];
let new_block = block::Block::new_block(data, pre_block.hash.clone());
self.blocks.push(new_block);
}
fn new_genesis_block() -> block::Block {
block::Block::new_block("This is genesis block".to_string(), String::from(""))
}
fn new_blockchain() -> BlockChain {
BlockChain {
blocks: vec![BlockChain::new_genesis_block()],
}
}
}
Define the main function:
fn main() {
let mut bc = blockchain::BlockChain::new_blockchain();
bc.add_block(String::from("a -> b: 5 btc"));
bc.add_block("c -> d: 1 btc".to_string());
for b in bc.blocks {
println!("++++++++++++++++++++++++++++++++++++++++++++");
println!("{:#?}", b);
println!("");
}
}
Finally, the code should look like this:
use bincode;
use serde::{Deserialize, Serialize};
use crypto::digest::Digest;
use crypto::sha3::Sha3;
use chrono::prelude::*;
struct BlockHeader {
time: i64,
tx_hash: String,
pre_hash: String,
}
struct Block {
header: BlockHeader,
hash: String,
data: String, //transactions data
}
impl Block {
fn set_hash(&mut self) {
let header = coder::my_serialize(&(self.header));
self.hash = coder::get_hash(&header[..]);
}
fn new_block(data: String, pre_hash: String) -> Block {
let transactions = coder::my_serialize(&data);
let tx_hash = coder::get_hash(&transactions[..]);
let time = Utc::now().timestamp();
let mut block = Block {
header: BlockHeader {
time: time,
tx_hash: tx_hash, //transactions data merkle root hash
pre_hash: pre_hash,
},
hash: "".to_string(),
data: data,
};
block.set_hash();
block
}
}
// Serialization
fn my_serialize<T: ?Sized>(value: &T) -> Vec<u8>
where T: Serialize,
{
let serialized = bincode::serialize(value).unwrap();
serialized
}
// Deserialization
fn my_deserialize<'a, T>(bytes: &'a[u8]) -> T
where T: Deserialize<'a>,
{
let deserialized = bincode::deserialize(bytes).unwrap();
deserialized
}
fn get_hash(value: &[u8]) -> String {
let mut hasher = Sha3::sha3_256();
hasher.input(value);
hasher.result_str()
}
struct BlockChain {
blocks: Vec<block::Block>,
}
impl BlockChain {
fn add_block(&mut self, data: String) {
let pre_block = &self.blocks[self.blocks.len() - 1];
let new_block = block::Block::new_block(data, pre_block.hash.clone());
self.blocks.push(new_block);
}
fn new_genesis_block() -> block::Block {
block::Block::new_block("This is genesis block".to_string(), String::from(""))
}
fn new_blockchain() -> BlockChain {
BlockChain {
blocks: vec![BlockChain::new_genesis_block()],
}
}
}
fn main() {
let mut bc = blockchain::BlockChain::new_blockchain();
bc.add_block(String::from("a -> b: 5 btc"));
bc.add_block("c -> d: 1 btc".to_string());
for b in bc.blocks {
println!("++++++++++++++++++++++++++++++++++++++++++++");
println!("{:#?}", b);
println!("");
}
}
Run cargo run
to see the result.