zoukankan      html  css  js  c++  java
  • [易学易懂系列|rustlang语言|零基础|快速入门|(27)|实战4:从零实现BTC区块链]

    [易学易懂系列|rustlang语言|零基础|快速入门|(27)|实战4:从零实现BTC区块链]

    项目实战

    实战4:从零实现BTC区块链

    我们今天来开发我们的BTC区块链系统。

    简单来说,从数据结构的角度上来说,区块链,就是区块组成的链。

    以下就是BTC区块链典型的结构:

    See the source image

    那最小单元就是区块:block。

    这个block包含两部分:区块头,区块体。

    我们先忽略Merkle树,先简化所有数据结构,只保留最基本的数据结构。

    那区块头,就包含:区块高度,时间截;前一个区块地址;工作证明;

    区块体,就包含交易数据,我们用一个vector来存储。

    代码如下 :

    ///交易结构体
    #[derive(Clone, Hash, Serialize, Deserialize, Debug)]
    pub struct Transaction {
        sender: String,    //发送者
        recipient: String, //接收者
        amount: i64,       //交易数量
    }
    /// 区块结构体
    #[derive(Clone, Hash, Serialize, Deserialize, Debug)]
    pub struct Block {
        pub index: u64,                     //区块高度
        timestamp: DateTime<Utc>,           //时间截
        pub transactions: Vec<Transaction>, //交易集合
        pub proof: u64,                     //证明
        pub previous_hash: String,          //上一个区块哈希地址
    }
    //区块链结构体
    #[derive(Default)]
    pub struct Blockchain {
        pub chain: Vec<Block>,                  //区块链帐本
        current_transactions: Vec<Transaction>, //当前交易集合
        pub nodes: HashSet<String>,             //节点集合
    }
    
    
    

    现在我们创建了一个基本的区块数据结构,现在我们来让矿工来创建区块吧。

    怎么让不同的矿工,积极地创建区块呢?

    我们引入一个机制叫:POW共识机制。

    什么叫POW?简单来说,就是大家根据给定的一个数值proof,进行hash计算,谁最先算出来的结果值符合某个条件,就拥有创建新的区块,并把这个区块连接到原来的区块链上的权力。

    比如,困难程度为5,那有个矿工用proof数据进行SHA哈希计算,出如下结果:

    0x0000010000000000000000000000000000000000000000000000000000000000

    这个结果,前面的0(除了0x外)是5个,则这就是结果值。

    如果,没有计算出上面的结果值,矿工将proof自增1,再进行SHA哈希计算,直到计算出这个符合条件的结果值为止。

    而那个给定的数据值proof,也要放在区块头,这个值在每次创建新区块的时候由矿工产生并写入区块头。

    当然,如果 两个节点都算出结果并加入了新区块,这时,会产生链的分叉,这时如何决定冲突呢?

    我们用最长链原则,即给定周期内,哪个节点拥有的链最长,就用哪个。

    所以我们的共识机制是:POW+最长链原则

    这个共识机制核心 代码如下:

    impl Blockchain {
        //创建创世区块
        pub fn new() -> Blockchain {
            let mut blockchain = Blockchain {
                chain: vec![],
                current_transactions: vec![],
                nodes: HashSet::new(),
            };
            blockchain.new_block(100, Some("1"));
            blockchain
        }
        /// Create a new Block in the Blockchain
        ///
        /// :param proof: The proof given by the Proof of Work algorithm
        /// :param previous_hash: (Optional) hash of previous Block
        /// :return: New Bloc
        /// 创建新区块
        pub fn new_block(&mut self, proof: u64, previous_hash: Option<&str>) -> Block {
            let block = Block {
                index: (self.chain.len() + 1) as u64,
                timestamp: Utc::now(),
                transactions: self.current_transactions.drain(0..).collect(),
                proof,
                previous_hash: previous_hash.unwrap_or("0").to_string(),
            };
    
            self.chain.push(block.clone());
            block
        }
        /// Creates a new transaction to go into the next mined Block
        ///
        /// :param sender: Address of the śender
        /// :param recipient: Address of the recipient
        /// :param amount: Amount
        /// :return: The index of the Block that will hold this transaction
        /// 发起一个新交易,将写入下一个区块
        pub fn new_transaction(&mut self, sender: &str, recipient: &str, amount: i64) -> u64 {
            self.current_transactions.push(Transaction {
                sender: sender.to_string(),
                recipient: recipient.to_string(),
                amount,
            });
            self.last_block().unwrap().index + 1
        }
        /// Simple Proof of Work Algorithm:
        /// - Find a number p' such that hash(pp') contains 4 leading zeroes,
        ///   where p is the previous proof, and p' is the new proof
        /// POW工作量证明共识机制算法
        pub fn proof_of_work(last_block: &Block) -> u64 {
            let mut proof = 0;
            let last_proof = last_block.proof;
            let last_hash = &last_block.previous_hash;
            while !Self::valid_proof(last_proof, proof, last_hash) {
                proof += 1;
            }
            proof
        }
        /// Validates the Proof: Does hash(last_proof, proof, last_hash) containt 4 leading zeroes
        //验证工作证明数字
        fn valid_proof(last_proof: u64, proof: u64, last_hash: &String) -> bool {
            let guess = format!("{}{}{}", last_proof, proof, last_hash);
            let guess_hash = hex_digest(Algorithm::SHA256, guess.as_bytes());
            guess_hash.ends_with("00000") //困难度为5
        }
    
        /// Creates a SHA-256 hash of a Block
        ///
        /// :param block: Block
        /// :return hash for the block
        /// 创建一个区块 的哈希值,基SHA-256算法
        pub fn hash(block: &Block) -> String {
            let serialized = serde_json::to_string(&block).unwrap();
            hex_digest(Algorithm::SHA256, serialized.as_bytes())
        }
        /// Returns the last Block in the chain
        /// 返回最后一个区块
        pub fn last_block(&self) -> Option<&Block> {
            self.chain.last()
        }
    
        /// Add a new node to the list of nodes
        ///
        /// :param address: Address of the node. Eg. 'http://192.168.0.5:5000'
        ///
        /// 节点注册,即新节点加入区块链网络,注册地址参数为节点服务器地址,如:'http://192.168.0.5:5000‘
        pub fn register_node(&mut self, address: &str) {
            let parsed_url = urlparse(address);
            self.nodes.insert(parsed_url.netloc);
        }
    
        /// Determine if a given blockchain is valid
        /// 链的验证
        fn valid_chain(&self, chain: &[Block]) -> bool {
            let mut last_block = &chain[0];
            let mut current_index: usize = 1;
            while current_index < chain.len() {
                let block = &chain[current_index];
                println!("{:?}", last_block);
                println!("{:?}", block);
                println!("-----------");
                if block.previous_hash != Blockchain::hash(last_block) {
                    return false;
                }
                if !Blockchain::valid_proof(last_block.proof, block.proof, &last_block.previous_hash) {
                    return false;
                }
    
                last_block = block;
                current_index += 1;
            }
            true
        }
    
        /// This is our Consensus Algorithm, it resolves conflicts
        /// by replacing our chain with the longest one in the network.
        ///
        /// :return True if our chain was replaced and false otherwise
        /// 解决冲突的机制,即共识机制,最长链原则处理逻辑,即共识机制为(POw+最长链原则)
        pub fn resolve_conflicts(&mut self) -> bool {
            let mut max_length = self.chain.len();
            let mut new_chain: Option<Vec<Block>> = None;
    
            // Grab and verify the chains from all the nodes in our network
            for node in &self.nodes {
                let mut response = reqwest::get(&format!("http://{}/chain", node)).unwrap();
                if response.status().is_success() {
                    let node_chain: Chain = response.json().unwrap();
                    if node_chain.length > max_length && self.valid_chain(&node_chain.chain) {
                        max_length = node_chain.length;
                        new_chain = Some(node_chain.chain);
                    }
                }
            }
            // Replace our chain if we discovered a new, valid chain longer than ours
            match new_chain {
                Some(x) => {
                    self.chain = x;
                    true
                }
                None => false,
            }
        }
    }
    
    

    以上代码,我们放在当前工程目录下的src/blockchain.rs,完整代码如下 :

    use crate::api::Chain;
    use chrono::{DateTime, Utc};
    use crypto_hash::{hex_digest, Algorithm};
    use reqwest;
    use serde::{Deserialize, Serialize};
    use std::collections::HashSet;
    use urlparse::urlparse;
    ///交易结构体
    #[derive(Clone, Hash, Serialize, Deserialize, Debug)]
    pub struct Transaction {
        sender: String,    //发送者
        recipient: String, //接收者
        amount: i64,       //交易数量
    }
    /// 区块结构体
    #[derive(Clone, Hash, Serialize, Deserialize, Debug)]
    pub struct Block {
        pub index: u64,                     //区块高度
        timestamp: DateTime<Utc>,           //时间截
        pub transactions: Vec<Transaction>, //交易
        pub proof: u64,                     //证明
        pub previous_hash: String,          //上一个区块哈希地址
    }
    //区块链结构体
    #[derive(Default)]
    pub struct Blockchain {
        pub chain: Vec<Block>,                  //区块链帐本
        current_transactions: Vec<Transaction>, //交易集合
        pub nodes: HashSet<String>,             //节点集合
    }
    
    impl Blockchain {
        //创建创世区块
        pub fn new() -> Blockchain {
            let mut blockchain = Blockchain {
                chain: vec![],
                current_transactions: vec![],
                nodes: HashSet::new(),
            };
            blockchain.new_block(100, Some("1"));
            blockchain
        }
        /// Create a new Block in the Blockchain
        ///
        /// :param proof: The proof given by the Proof of Work algorithm
        /// :param previous_hash: (Optional) hash of previous Block
        /// :return: New Bloc
        /// 创建新区块
        pub fn new_block(&mut self, proof: u64, previous_hash: Option<&str>) -> Block {
            let block = Block {
                index: (self.chain.len() + 1) as u64,
                timestamp: Utc::now(),
                transactions: self.current_transactions.drain(0..).collect(),
                proof,
                previous_hash: previous_hash.unwrap_or("0").to_string(),
            };
    
            self.chain.push(block.clone());
            block
        }
        /// Creates a new transaction to go into the next mined Block
        ///
        /// :param sender: Address of the śender
        /// :param recipient: Address of the recipient
        /// :param amount: Amount
        /// :return: The index of the Block that will hold this transaction
        /// 发起一个新交易,将写入下一个区块
        pub fn new_transaction(&mut self, sender: &str, recipient: &str, amount: i64) -> u64 {
            self.current_transactions.push(Transaction {
                sender: sender.to_string(),
                recipient: recipient.to_string(),
                amount,
            });
            self.last_block().unwrap().index + 1
        }
        /// Simple Proof of Work Algorithm:
        /// - Find a number p' such that hash(pp') contains 4 leading zeroes,
        ///   where p is the previous proof, and p' is the new proof
        /// POW工作量证明共识机制算法
        pub fn proof_of_work(last_block: &Block) -> u64 {
            let mut proof = 0;
            let last_proof = last_block.proof;
            let last_hash = &last_block.previous_hash;
            while !Self::valid_proof(last_proof, proof, last_hash) {
                proof += 1;
            }
            proof
        }
        /// Validates the Proof: Does hash(last_proof, proof, last_hash) containt 4 leading zeroes
        //验证工作证明数字
        fn valid_proof(last_proof: u64, proof: u64, last_hash: &String) -> bool {
            let guess = format!("{}{}{}", last_proof, proof, last_hash);
            let guess_hash = hex_digest(Algorithm::SHA256, guess.as_bytes());
            guess_hash.ends_with("00000") //困难度为5
        }
    
        /// Creates a SHA-256 hash of a Block
        ///
        /// :param block: Block
        /// :return hash for the block
        /// 创建一个区块 的哈希值,基SHA-256算法
        pub fn hash(block: &Block) -> String {
            let serialized = serde_json::to_string(&block).unwrap();
            hex_digest(Algorithm::SHA256, serialized.as_bytes())
        }
        /// Returns the last Block in the chain
        /// 返回最后一个区块
        pub fn last_block(&self) -> Option<&Block> {
            self.chain.last()
        }
    
        /// Add a new node to the list of nodes
        ///
        /// :param address: Address of the node. Eg. 'http://192.168.0.5:5000'
        ///
        /// 节点注册,即新节点加入区块链网络,注册地址参数为节点服务器地址,如:'http://192.168.0.5:5000‘
        pub fn register_node(&mut self, address: &str) {
            let parsed_url = urlparse(address);
            self.nodes.insert(parsed_url.netloc);
        }
    
        /// Determine if a given blockchain is valid
        /// 链的验证
        fn valid_chain(&self, chain: &[Block]) -> bool {
            let mut last_block = &chain[0];
            let mut current_index: usize = 1;
            while current_index < chain.len() {
                let block = &chain[current_index];
                println!("{:?}", last_block);
                println!("{:?}", block);
                println!("-----------");
                if block.previous_hash != Blockchain::hash(last_block) {
                    return false;
                }
                if !Blockchain::valid_proof(last_block.proof, block.proof, &last_block.previous_hash) {
                    return false;
                }
    
                last_block = block;
                current_index += 1;
            }
            true
        }
    
        /// This is our Consensus Algorithm, it resolves conflicts
        /// by replacing our chain with the longest one in the network.
        ///
        /// :return True if our chain was replaced and false otherwise
        /// 最长链原则处理逻辑,即共识机制为(POw+最长链原则)
        pub fn resolve_conflicts(&mut self) -> bool {
            let mut max_length = self.chain.len();
            let mut new_chain: Option<Vec<Block>> = None;
    
            // Grab and verify the chains from all the nodes in our network
            for node in &self.nodes {
                let mut response = reqwest::get(&format!("http://{}/chain", node)).unwrap();
                if response.status().is_success() {
                    let node_chain: Chain = response.json().unwrap();
                    if node_chain.length > max_length && self.valid_chain(&node_chain.chain) {
                        max_length = node_chain.length;
                        new_chain = Some(node_chain.chain);
                    }
                }
            }
            // Replace our chain if we discovered a new, valid chain longer than ours
            match new_chain {
                Some(x) => {
                    self.chain = x;
                    true
                }
                None => false,
            }
        }
    }
    
    

    现在 我们向外界提供一些可用的API。

    我们新建一个文件:src/api.rs,代码如下 :

    use crate::blockchain::{Block, Blockchain, Transaction};
    
    use actix_web::{web, HttpRequest, HttpResponse};
    use serde::{Deserialize, Serialize};
    use std::sync::Mutex;
    ///返回消息体
    #[derive(Serialize, Deserialize)]
    pub struct MessageResponse {
        message: String,
    }
    //交易请求信息
    #[derive(Serialize, Deserialize)]
    pub struct TransactionRequest {
        sender: String,
        recipient: String,
        amount: i64,
    }
    ///挖矿响应消息
    #[derive(Serialize)]
    pub struct MiningRespose {
        message: String,
        index: u64,
        transactions: Vec<Transaction>,
        proof: u64,
        previous_hash: String,
    }
    ///链结构体,代表现在网络上的最长链
    #[derive(Serialize, Deserialize)]
    pub struct Chain {
        pub chain: Vec<Block>,
        pub length: usize,
    }
    ///节点注册请求信息
    #[derive(Deserialize)]
    pub struct RegisterRequest {
        nodes: Vec<String>,
    }
    ///节点注册响应信息
    #[derive(Serialize)]
    pub struct RegisterResponse {
        message: String,
        total_nodes: Vec<String>,
    }
    //解决冲突响应信息
    #[derive(Serialize)]
    pub struct ResolveResponse {
        message: String,
        chain: Vec<Block>,
    }
    ///发起新交易
    pub fn new_transaction(
        state: web::Data<Mutex<Blockchain>>,
        req: web::Json<TransactionRequest>,
    ) -> HttpResponse {
        let sender = req.sender.to_owned();
        let recipient = req.recipient.to_owned();
        let index = state
            .lock()
            .unwrap()
            .new_transaction(&sender, &recipient, req.amount);
        HttpResponse::Created().json(MessageResponse {
            message: format! {"Transaction will be added to Block {}", index},
        })
    }
    ///矿工挖矿
    pub fn mine(
        node_identifier: web::Data<String>,
        state: web::Data<Mutex<Blockchain>>,
        _req: HttpRequest,
    ) -> HttpResponse {
        let (proof, previous_hash) = {
            let blockchain = state.lock().unwrap();
            let last_block = blockchain.last_block().unwrap();
            let proof = Blockchain::proof_of_work(&last_block);
            let previous_hash = Blockchain::hash(last_block);
            (proof, previous_hash)
        };
        let mut blockchain = state.lock().unwrap();
        blockchain.new_transaction("0", &*node_identifier, 1);
        let block = blockchain.new_block(proof, Some(&previous_hash));
        HttpResponse::Ok().json(MiningRespose {
            message: "New Block Forged".to_string(),
            index: block.index,
            transactions: block.transactions,
            proof,
            previous_hash,
        })
    }
    ///当前最新链的信息
    pub fn chain(state: web::Data<Mutex<Blockchain>>, _req: HttpRequest) -> HttpResponse {
        let length = state.lock().unwrap().chain.len();
        HttpResponse::Ok().json(Chain {
            chain: state.lock().unwrap().chain.clone(),
            length,
        })
    }
    ///节点注册
    pub fn register_node(
        state: web::Data<Mutex<Blockchain>>,
        req: web::Json<RegisterRequest>,
    ) -> HttpResponse {
        if req.nodes.is_empty() {
            return HttpResponse::BadRequest().json(MessageResponse {
                message: "Error: Please supply a valid list of nodes".to_string(),
            });
        }
        let mut blockchain = state.lock().unwrap();
        for node in req.nodes.iter() {
            blockchain.register_node(node)
        }
        HttpResponse::Created().json(RegisterResponse {
            message: "New nodes have been added".to_string(),
            total_nodes: blockchain.nodes.iter().cloned().collect(),
        })
    }
    ///跟网络上其他节点达成共识,即解决冲突
    pub fn resolve_nodes(state: web::Data<Mutex<Blockchain>>, _req: HttpRequest) -> HttpResponse {
        let mut blockchain = state.lock().unwrap();
        let replaced = blockchain.resolve_conflicts();
        let message = if replaced {
            "Our chain was replaced"
        } else {
            "Our chain is authorative"
        };
        HttpResponse::Ok().json(ResolveResponse {
            message: message.to_string(),
            chain: blockchain.chain.clone(),
        })
    }
    
    

    当然,我们要用到一些好用的库,在我们的Cargo.toml文件,我们加入依赖,完整代码如下:

    [dependencies]
    chrono = { version = "0.4.6", features = ["serde"] }
    crypto-hash = "0.3.3"
    serde = { version = "1.0.90", features = ["derive"] }
    serde_json = "1.0"
    actix-web = "1.0"
    uuid = { version = "0.7", features = ["v4"] }
    urlparse = "0.7.3"
    reqwest = "=0.9.17"
    

    最后我们的主程序 src/main.rs如下:

    pub mod api;
    pub mod blockchain;
    
    use actix_web::{web, App, HttpServer};
    use std::env;
    use std::sync::Mutex;
    use uuid::Uuid;
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let port = match args.as_slice() {
            [_, key, value] => {
                if key == "--p" {
                    value
                } else {
                    panic!("Illegal arguments passed to the program.");
                }
            }
            _ => "5000",
        };
        // TODO: make chain shared across threads
        let sharedchain = web::Data::new(Mutex::new(blockchain::Blockchain::new()));
        let node_identifier = web::Data::new(Uuid::new_v4().to_simple().to_string());
    
        HttpServer::new(move || {
            App::new()
                .register_data(sharedchain.clone())
                .register_data(node_identifier.clone())
                .data(web::JsonConfig::default().limit(4096))
                .service(web::resource("/mine").route(web::get().to(api::mine)))
                .service(web::resource("/transactions/new").route(web::post().to(api::new_transaction)))
                .service(web::resource("/chain").route(web::get().to(api::chain)))
                .service(web::resource("/nodes/register").route(web::post().to(api::register_node)))
                .service(web::resource("/nodes/resolve").route(web::get().to(api::resolve_nodes)))
        })
        .bind(format!("127.0.0.1:{}", port))
        .unwrap()
        .run();
    }
    
    

    然后我们可以用以下命令来调用 :

    挖矿:

    curl http://localhost:5000/mine
    

    创建新交易:

    curl -H "Content-Type: application/json" --request POST --data '{"sender":"e79fcabd1d70433191701d17c4d13112", "recipient":"some-other-address", "amount":5}' http://localhost:5000/transactions/new
    

    查看整条链信息:

    curl http://localhost:5000/chain
    
    

    注册节点:

    curl -H "Content-Type: application/json" --request POST --data '{"nodes":["http://localhost:5001"]}' http://localhost:5000/nodes/register
    
    

    与其他节点达成共识(共识机制):

    curl http://localhost:5000/nodes/resolve
    
    

    以上,希望对你有用。

    如果遇到什么问题,欢迎加入:rust新手群,在这里我可以提供一些简单的帮助,加微信:360369487,注明:博客园+rust
    
    

    https://asymmetric.github.io/2018/02/11/blockchain-rust/

    https://jeiwan.net/posts/building-blockchain-in-go-part-1/

    https://freestartupkits.com/articles/technology/cryptocurrency-news-and-tips/ultimate-rust-blockchain-tutorial/

    https://hackernoon.com/learn-blockchains-by-building-one-117428612f46

    https://medium.com/@vanflymen/learn-blockchains-by-building-one-117428612f46?

    https://github.com/Koura/blockchain-example

  • 相关阅读:
    积水路面Wet Road Materials 2.3
    门控时钟问题
    饮料机问题
    Codeforces Round #340 (Div. 2) E. XOR and Favorite Number (莫队)
    Educational Codeforces Round 82 (Rated for Div. 2)部分题解
    Educational Codeforces Round 86 (Rated for Div. 2)部分题解
    Grakn Forces 2020部分题解
    2020 年百度之星·程序设计大赛
    POJ Nearest Common Ancestors (RMQ+树上dfs序求LCA)
    算法竞赛进阶指南 聚会 (LCA)
  • 原文地址:https://www.cnblogs.com/gyc567/p/12079503.html
Copyright © 2011-2022 走看看