Python 模擬簡單區塊鏈

首先這是說明一下這是Tiny熊老師的教程https://www.cnblogs.com/tinyxionghtml

另外還要說明一下,暑假指導老師讓咱們作一些關於區塊鏈的應用。這裏只是涉及極其簡單的模擬,主要是記錄這些天本身學習的知識。node

什麼是區塊鏈?算法

下面簡單說一下區塊鏈是什麼,作個比喻,區塊就像一我的,區塊鏈比如一羣人。怎麼才能讓一羣人聯繫起來哪,就必須讓他們之間有必定的關係和聯繫。好比,第一我的有100塊錢,存入銀行。銀行給了他一張「憑證」,可是這張憑證被第二我的拿走了,第三我的的憑證被第四個。。。。mongodb

如此一來這麼多人就被聯繫起來。我們此次實現的簡單的區塊鏈就是本區塊帶有上一區塊的哈希。數據庫

先簡單定義一下區塊的內容:json

# {
#     "index": 1,           區塊的塊號
#     "timestamp": "",      時間戳
#     "transactions": [     交易內容
#         {
#           "sender": "",
#           "recipient": "",
#           "amount": 5,
#         }
#     ],
#     "proof": "",          工做量證實
#     "previous_hash":""    上一個區塊的hash
#
# }

本次才用的是Python Flask框架,使用雲端MongoDB ,https://cloud.mongodb.com/註冊申請這裏就不詳細說了。flask

簡單說一下須要準備的有,PyCharm , pip , Python 3.7。app

使用PyCharm 建立一個PyThon虛擬環境 。點擊Create New Project 。選擇文件夾,默認選項就是在本文件夾安裝虛擬環境。框架

而後就是各類包ide

import hashlib  # hash 算法
import json     # josn
from time import time #  時間戳
from uuid import uuid4  # uuid爲本機生成ID
from flask import Flask, jsonify, request 
import pymongo

咱們設想一下,數據要保存在什麼地方纔能在下次啓動程序的時候繼續按照上一次結束的數據進行下一次的運算。所以咱們須要使用數據庫保存咱們須要保存的數據。因此咱們要先鏈接數據庫。

#  **User**:**password** 這是你建立集羣的用戶和密碼
client = pymongo.MongoClient('mongodb+srv://**User**:**password**@iec-pj8qn.mongodb.net/MainSite') db = client.MainSite # collection = db.blockchain

如今數據庫已經鏈接上了,可是問題來了。咱們怎麼取出最底層的文檔哪?下面咱們須要一個循環遍歷集合的最大值,回想一下咱們定義的區塊結構。裏面定義的 index:1 。 每次新增一個區塊,第二個區塊的index = 2 . 一次增長下去。這樣遍歷的最大值,也能夠說是遍歷的次數就是咱們須要尋找的index:last

,也就是最後一次被插入的數據。MongoDB 在沒有給定特定的_id 字段時,本身會生成一個相似與時間戳的字段。這不是咱們須要的,咱們在取出數據的時候要把他剔除。

class value:
    # 取出文檔的數據再次組合並存儲在current[] 列表裏
    def value(self, index1, hash1, proof, transactions1, timestamp) -> list:
        current = []
        json_value = {
            'index': index1,
            'previous_hash': hash1,
            'proof': proof,
            'transactions': transactions1,
            'timestamp': timestamp
        }
        current.append(json_value)
        return current
class counting:       # 循環遍歷集合最大值
    def count(self):
        last_count = 0
        for x in collection.find():  # collection.find() 集合的全部文檔
            last_count = last_count + 1
        return last_count


last1 = counting() # 調用counting類
last = last1.count()
print(last)
result = collection.find_one({"index": last})  # 搜索到最後一個文檔
value = value()  # 建立value對象
chain0 = value.value(result['index'],
                     result['previous_hash'],
                     result['proof'],
                     result['transactions'],
                     result['timestamp'])      # dict 轉 list
print(chain0)
print(type(chain0))

client.close() # 鏈接斷開

如今咱們已經獲取都最近一次插入的數據。咱們如今就能夠插入創始區塊了:

{
      "index": 1,
      "previous_hash": 1,
      "proof": 100,
      "timestamp": 1541940889.5927348,
      "transactions": []
    }

把這段josn插入MongoDB , 或許你能夠在網頁插入或者在本地下載一個 MongoDB Shell 這個雲端MongoDB 有提示怎麼下載,這裏我就很少說了。若是不明白MongoDB 的用法請去 菜鳥教程

把創始區塊插入集合,啓動一下。出現你插入的數據,說明你已經成功的鏈接數據庫。

下面咱們來看一大段代碼:

class Blockchain:

    def __init__(self):  # 構造函數,初始區塊鏈,當前交易,生成創始區塊
        self.chain = []  # 真正的區塊鏈
        self.chain.append(chain0[0])     # 每次鏈接取最後一個集合文檔做爲本次的啓動創世區塊
        self.current_transactions = []
        #  self.new_block(proof=100, previous_hash=1) # 若是沒有用到數據庫,調用構造函數,自行建立一個創世區塊

    def new_block(self, proof, previous_hash=None, last_index=None):  # 新建區塊 工做量證實,前一個區塊Hash
        #  定義交易區塊實體
        block = {
            'index': last_index + 1,
            'timestamp': time(),
            'transactions': self.current_transactions,  # 當前交易
            'proof': proof,
            'previous_hash': previous_hash or self.hash(self.last_block)
        }
        self.current_transactions = []  # 清空當前交易
        self.chain.append(block)  # 區塊鏈添加新區塊
        return block

    def new_transactions(self, sender, recipient, amount) -> int:  # 新的交易
        self.current_transactions.append(  # 當前交易添加數據
            {
                'sender': sender,
                'recipient': recipient,
                'amount': amount
            }
        )

        return self.last_block['index'] + 1  # 最後一個區塊的 index+1

    @staticmethod
    def hash(block):  # 區塊hash算法
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()

    @property
    def last_block(self):  # 最後一個區塊# 取出的最後一個區塊類型老是 list
        long = len(self.chain)
        print(long)
        if long > 1:
            last_block = self.chain[-1]
            print('++++++++++++++++++++++++')
            print(last_block)
            print(type(last_block))
            print(last_block['index'])
            temp_json = {
                'index': last_block['index'],
                'previous_hash': last_block['previous_hash'],
                'proof': last_block['proof'],
                'transactions': last_block['transactions'],
                'timestamp': last_block['timestamp']
            }
            print(temp_json)
            self.chain.append(temp_json)
        print(self.chain)
        print(type(self.chain[-1]))
        return self.chain[-1]

    def proof_of_work(self, last_proof: int) -> int:  # 工做量證實
        proof = 0
        while self.valid_proof(last_proof, proof) is False:  # 循環檢測合格hash
            proof += 1
        # print(proof)
        return proof

    def valid_proof(self, last_proof: int, proof: int) -> bool:  # 有效工做量證實
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()  # 哈希後獲得摘要
        # print(guess_hash)
        if guess_hash[0:4] == "0000":  # 工做量證實條件
            return True
        else:
            return False

上面的類,基本上都有註釋,而且時我測試時的斷點也有保留,更清晰的瞭解數據的類型和數值。我就不一一口述了。簡單的說一下就是 交易方法def new_transactions, 和 新建塊的打包計算def new_block

計算hash def hash(block):  有效工做量證實def hash(block)

本地文件夾建立static 文件夾  加入index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<p>Hello BlockChain</p>
<form action="transactions" method="post">
    sender:<input type="text" name="sender">
    recipient:<input type="text" name="recipient">
    amount:<input type="text" name="amount">
    <input type="submit" value="submit">
</form>

</body>
</html>

下面開始使用咱們的Flask框架

app = Flask(__name__, static_url_path='') # 參數的意思是爲靜態html文件,添加路徑

Flask框架

app = Flask(__name__, static_url_path='') # 參數的意思是爲靜態html文件,添加路徑
blockchain = Blockchain() # 建立對象

node_identifier = str(uuid4()).replace('-', '') # 使用uuid生成本結點ID,replace()替換'-'


@app.route('/', methods=['GET']) def index(): return app.send_static_file('index.html') @app.route('/transactions', methods=['POST']) def new_transaction(): # 新的交易 print(request) sender = request.form['sender'] # 取出 Form 裏的值 print(sender) recipient = request.form['recipient'] print(recipient) amount = request.form['amount'] print(amount) values = [sender, recipient, amount] index = blockchain.new_transactions(values[0], # 調用函數 values[1], values[2]) response = {"message": f'Transaction will be added to Block {index}'} return jsonify(response), 201 @app.route('/mine', methods=['GET']) def mine(): # 交易打包,挖礦 last_block = blockchain.last_block print("=======================") print(type(last_block)) print(last_block) last_proof = last_block['proof'] print(last_proof) last_index = last_block['index'] print(last_index) proof = blockchain.proof_of_work(last_proof) # 工做量證實 也就是挖礦 blockchain.new_transactions(sender="0", # 給本結點的獎勵 recipient=node_identifier, amount=1) block = blockchain.new_block(proof, None, last_index) # 打包區塊 client = pymongo.MongoClient('mongodb+srv://**user**:**password**@iec-pj8qn.mongodb.net/MainSite') db = client.MainSite collection = db.blockchain collection.insert_one(block) # 把打包好的區塊添加到數據庫。 client.close() response = { "message": "New Block Forged", "index": block['index'], "transactions": block['transactions'], "proof": block['proof'], "previous_hash": block['previous_hash'] } return jsonify(response), 200 @app.route('/chain', methods=['GET']) def full_chain(): # 返回區塊鏈 response = { 'chain': blockchain.chain, 'length': len(blockchain.chain) } return jsonify(response), 200 if __name__ == '__main__': # 當 block chain.py 做爲模塊時不執行下面函數 app.run(host='127.0.0.1', port=3000)

把全部的代碼塊合在一塊兒就是本次區塊鏈模擬器的所有內容了,這只是單節點,單保存的鏈,只是更好的去理解區塊鏈的結構,還有好比共識機制和選擇鏈咱們尚未去完成。

對了 說一下用法:

 打開 http://127.0.0.1:3000/  

提交後: http://127.0.0.1:3000/mine

 

以後打開 PowerShell

結束。

相關文章
相關標籤/搜索