Python從零開始構建區塊鏈之數據層理解

#前言 比特幣在2017可謂是十足地火爆了,那他背後依賴的區塊鏈技術又是如何實現的呢?當下對區塊鏈人才的需求更是迫切中的迫切,或許在2018將火爆各個行業。html

本次系列文章將從實際代碼出發,來構建你對區塊鏈技術的認知。python

#寫代碼以前 ###基礎技能要求git

1.簡單的Python基礎
2.對HTTP請求有基本的認知
3.面向對象編程思惟
4.區塊鏈基本定義
複製代碼

###開發環境準備 #####1.Python3.6 Mac自帶的Python爲2.7,這裏咱們須要從新安裝Python3.6 1.1確保電腦安裝了套件管理工具 Homebrew,若是沒有請在命令行執行如下命令安裝:github

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
複製代碼

1.2 驗證是否安裝成功,該命令也能夠檢驗電腦是否安裝了Homebrew編程

brew doctor
複製代碼

1.3安裝Python3.6flask

brew install python3
複製代碼

1.4查看Python路徑ruby

// 系統自帶的python2.7,目錄爲/usr/bin/python
which python
//brew安裝的python3.4,目錄爲/usr/local/bin/python3
which python3
複製代碼

1.4查看Python路徑.png
1.5使用

// 系統自帶的
python a.py
//brew安裝的
python3 a.py
複製代碼

#####2.python包管理工具:Pip 通常咱們安裝python3時自帶了Pip,若是沒有在命令行用HomeBrew安裝:bash

brew install pip
複製代碼

2.1 配置pipenv:網絡

//安裝 pipenv
pip install pipenv 
複製代碼
//建立virtual env
pipenv --python=python3.6
複製代碼
//安裝依賴
pipenv install
複製代碼

#####3.Python IDE 這一項不是必須的,咱們能夠在命令行vi或記事本里寫python代碼。固然習慣使用IDE的童鞋也能夠選擇使用IDE,筆者這裏使用的Python IDE爲Pycharm專業版(自行百度PoJie教程)數據結構

#####4.flask網絡框架 咱們後期理解了區塊鏈的底層數據結構後,在網絡層實現節點同步和網絡共識時會使用到flask網絡框架。這裏,暫且提一下,本文內容暫時不會用到。

#####5.接口調試工具 同時後期進行區塊鏈網絡編程時還須要一個HTTP客戶端,好比Postman,cURL或其它客戶端。這裏我使用的是Postman )

##廢話少說擼代碼

咱們知道區塊鏈是有一個個區塊構成,而區塊內又包含了基本的區塊信息和若干個交易信息,一個交易信息就是對一筆交易的結構封裝(附帶了哈希值等值以防止交易被篡改)。咱們今天就從區塊鏈的最下層數據結構交易開始層層分析,直到構成一個完整的區塊鏈。

打開Pycharm新建一個Python項目並新建一個文件:blockchain.py(使用Vi或記事本開發的直接新建該文件)

####交易類 Transaction用來簡單描述一筆交易的主要信息

class Transaction: #交易類

    def __init__(self,
                 payer,   #付款方
                 recer,   #收款方
                 count):  #金額
        self.payer = payer
        self.recer = recer
        self.count = count
        self.timestamp = datetime.datetime.now()

    def __repr__(self):

        return str(self.payer) + " pay" + str(self.recer) + " " + str(self.count) + " in " + str(self.timestamp)
複製代碼

####交易記錄類 ChaorsMessage類用來封裝一筆交易,並引入哈希加密機制防止交易數據與時間或者交易鏈被篡改。

#交易鏈的簡單實現
ChaorsMessage
import datetime  #獲取時間的庫
import hashlib  #哈希函數庫
from Transaction import Transaction  #引入交易類

class ChaorsMessage: #交易記錄類

    def __init__(self, data):

        self.data = data   # 交易信息

        self.hash = None  #自身哈希
        self.prev_hash = None  #上一個交易記錄的哈希
        self.timestamp = datetime.datetime.now()
        self.payload_hash = self._hash_payload()    #鎖定哈希

    def _hash_payload(self):  #交易哈希

        return hashlib.sha256((str(self.timestamp) + str(self.data)).encode("utf-8")).hexdigest()

    def _hash_message(self):  #交易記錄哈希,鎖定交易(哈希再哈希)

        return hashlib.sha256((str(self.prev_hash) + str(self.payload_hash)).encode("utf-8")).hexdigest()

#密封,至關於將交易信息封裝爲一個帶哈希驗證值的數據結構 使得交易信息(包括交易數據和時間,交易連接的順序)不能被修改
    def seal(self): 
        self.hash = self._hash_message()  #對應數據鎖定

    def validate(self):  #驗證交易記錄是否合法

        if self.payload_hash != self._hash_payload():
            raise InvalidMessage("交易數據與時間被修改" + str(self))

        if self.hash != self._hash_message():  #判斷消息鏈
            raise InvalidMessage("交易的哈希連接被修改" + str(self))

        return "data ok" + str(self)

    def __repr__(self):  #返回對象基本信息

        mystr = "hash:{}, prev_hash:{}, data:{}".format(self.hash, self.prev_hash, self.data)
        return mystr

    def link(self, message):  #連接
        self.prev_hash = message.hash

class InvalidMessage(Exception):  #異常處理類

    def __init__(self, *args, **kwargs):
        Exception.__init__(self, *args, **kwargs)

f __name__ == '__main__':  #單獨模塊測試
    try:
        t1 = Transaction("chaors", "yajun", 999999999)
        t2 = Transaction("chaors2", "yajun2", 999999999)
 
        m1 = ChaorsMessage(t1)
        m2 = ChaorsMessage(t2)

        #交易密封
        m1.seal()
        #交易哈希只有密封以後才能link
        m2.link(m1)
        m2.seal()
        
        m1.validate()
        m2.validate()

        #篡改數據 篡改數據後會捕獲到異常
        # m2.data = "hahahaha"
        # m2.validate()
        #
        m2.prev_hash = "kkkkk"
        # print(m2)
        m2.validate()
    except InvalidMessage as e:
        print(e)
複製代碼

####Block類 每一個區塊包含屬性:索引(index),Unix時間戳(timestamp),交易列表(transactions),工做量證實(下次講這個,這裏暫時擱置)以及前一個區塊的Hash值。

如下是一個區塊的結構:

block = {
    'index': 1,
    'timestamp': 1506057125.900785,
    'transactions': [
        {
            'sender': "8527147fe1f5426f9dd545de4b27ee00",
            'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",
            'amount': 5,
        }
    ],
    'proof': 324984774000,
    'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
}
複製代碼

到這裏,區塊鏈的概念就清楚了,每一個新的區塊都包含上一個區塊的Hash,這是關鍵的一點,它保障了區塊鏈不可變性。若是攻擊者破壞了前面的某個區塊,那麼後面全部區塊的Hash都會變得不正確。

import datetime
import hashlib

from Message import ChaorsMessage
from Message import InvalidMessage
from Transaction import Transaction

class Block:

   def __init__(self, *args):

       self.messagelist = []  #存儲多個交易記錄
       self.timestamp = None  #當前時間戳
       self.hash = None;
       self.prev_hash = None

       #遍歷交易參數,將全部交易加入到交易列表內
       if args:
           for arg in args:
               self.add_message(arg)

   def add_message(self, msg):  #增長交易信息
       #區分第一條和後面的 判斷是否須要連接
       if len(self.messagelist) > 0:
           msg.link(self.messagelist[-1])
       msg.seal()
       msg.validate()
       self.messagelist.append(msg)

   def link(self, block):  #連接
     #當前區塊的上個哈希值爲上個區塊哈希值,將區塊鏈接起來
         block.hash = self.prev_hash

   def seal(self):  #區塊封裝,帶有時間戳和哈希值的數據結構
       self.timestamp = datetime.datetime.now()
       self.hash = self._hash_block()

 #求區塊的哈希值
   def _hash_block(self):
       return hashlib.sha256((str(self.prev_hash) +
                              str(self.timestamp) +
                              str(self.messagelist[-1].hash)).encode("utf-8")).hexdigest()

   def validate(self):  #區塊合法性驗證
       for i, msg in enumerate(self.messagelist):
           msg.validate()
           if i > 0 and msg.prev_hash != self.messagelist[i-1].hash:
               raise InvalidBlock("無效block,第{}條交易記錄被修改".format(i)+ str(self))

       return str(self) + "block ok..."

   def __repr__(self):
       return "block = hash:{}, prehash:{}, len:{}, time:{}".format(self.hash,
                                                                    self.prev_hash,
                                                                    len(self.messagelist),
                                                                    self.timestamp)
class InvalidBlock(Exception):  #異常處理類

   def __init__(self, *args, **kwargs):
       Exception.__init__(self, *args, **kwargs)

if __name__ == '__main__':

   try:
       t1 = Transaction("chaors", "yajun", 999999999)
       t2 = Transaction("chaors2", "yajun2", 999999999)
       t3 = Transaction("chaors4", "yajun4", 999999999)

       m1 = ChaorsMessage(t1)
       m2 = ChaorsMessage(t2)
       m3 = ChaorsMessage(t3)

       block = Block(m1, m2, m3)
       block.seal()
       print(block)
       # m1.data = "kkkk"
       block.messagelist[1] = m3
       block.validate()

   except InvalidMessage as e:
       print(e)

   except InvalidBlock as e:
       print(e)
複製代碼

####Blockchain類 Blockchain類用來管理鏈條,它能存儲交易,加入新塊等。它就是一個完整的區塊鏈

import datetime
import hashlib

from Block import Block
from Block import InvalidBlock
from Message import ChaorsMessage
from Message import InvalidMessage
from Transaction import Transaction

class BlockChain:

    def __init__(self):
        self.blocklist = []

    def add_block(self, block):
        if len(self.blocklist) > 0:
            block.prev_hash = self.blocklist[-1].hash
        block.seal()  #區塊封裝
        block.validate()  #區塊連接
        self.blocklist.append(block)

    def validate(self):  #區塊驗證
        for i, block in enumerate(self.blocklist):
            try:
                block.validate()
            except InvalidBlock as e:
                print(e)
                raise InvalidBlockChain("第{}區塊校驗錯誤".format(i))

    def __repr__(self):
        return "BlockChain:{}".format(len(self.blocklist))

class InvalidBlockChain(Exception):  # 異常處理類
   def __init__(self, *args, **kwargs):
        Exception.__init__(self, *args, **kwargs)

if __name__ == '__main__':
    try:
        t1 = Transaction("chaors", "yajun", 999999999)
        t2 = Transaction("chaors2", "yajun2", 999999999)
        t3 = Transaction("chaors4", "yajun4", 999999999)

        m1 = ChaorsMessage(t1)
        m2 = ChaorsMessage(t2)
        m3 = ChaorsMessage(t3)

        block1 = Block(m1, m2, m3)
        block1.seal()

        t21 = Transaction("chaors", "yajun", 999999999)
        t22 = Transaction("chaors2", "yajun2", 999999999)

        m21 = ChaorsMessage(t21)
        m22 = ChaorsMessage(t22)

        block2 = Block(m21, m22)
        block2.seal()

        t31 = Transaction("chaors", "yajun", 999999999)
        t32 = Transaction("chaors2", "yajun2", 999999999)
        t33 = Transaction("chaors4", "yajun4", 999999999)
        t34 = Transaction("chaors8", "yajun8", 999999999)

        m31 = ChaorsMessage(t31)
        m32 = ChaorsMessage(t32)
        m33 = ChaorsMessage(t33)
        m34 = ChaorsMessage(t34)

        block3 = Block(m31, m32, m33, m34)
        block3.seal()

        mychain = BlockChain()
        mychain.add_block(block1)
        mychain.add_block(block2)
        mychain.add_block(block3)

        print(mychain)
        #篡改區塊
        block3.messagelist[1] = m33
        # m31.data = "lkjioh"
        mychain.validate()

    except InvalidBlockChain as e:
        print(e)
複製代碼

經過上面的代碼,能夠對區塊鏈區塊的產生和交易有一個更深入的瞭解。交易和存儲交易的區塊由於哈希值都會具備不可篡改的特性。

固然真正的區塊鏈遠不止這麼簡單,這裏只是大概搭建一個簡單的區塊鏈Demo,用於理解區塊鏈的不可篡改原理和基本數據結構。本文代碼也只實現了區塊鏈的數據層,至於網絡層,共識層等,之後有機會再寫一寫。

源代碼在這裏

#下一篇:用Python從零開始構建區塊鏈之網絡+共識(一)

###互聯網顛覆世界,區塊鏈顛覆互聯網!

---------------------------------------------------------20180405夜
相關文章
相關標籤/搜索