Python從零開始構(gòu)建區(qū)塊鏈之?dāng)?shù)據(jù)層理解

前言

比特幣在2017可謂是十足地火爆了初橘,那他背后依賴的區(qū)塊鏈技術(shù)又是如何實(shí)現(xiàn)的呢块差?當(dāng)下對區(qū)塊鏈人才的需求更是迫切中的迫切杖虾,或許在2018將火爆各個(gè)行業(yè)。

本次系列文章將從實(shí)際代碼出發(fā)蝇裤,來構(gòu)建你對區(qū)塊鏈技術(shù)的認(rèn)知徒探。

寫代碼之前

基礎(chǔ)技能要求

1.簡單的Python基礎(chǔ)
2.對HTTP請求有基本的認(rèn)知
3.面向?qū)ο缶幊趟季S
4.區(qū)塊鏈基本定義

開發(fā)環(huán)境準(zhǔn)備

1.Python3.6

Mac自帶的Python為2.7腊凶,這里我們需要重新安裝Python3.6
1.1確保電腦安裝了套件管理工具 Homebrew编矾,如果沒有請?jiān)诿钚袌?zhí)行以下命令安裝:

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

1.2 驗(yàn)證是否安裝成功,該命令也可以檢驗(yàn)電腦是否安裝了Homebrew

brew doctor

1.3安裝Python3.6

brew install python3

1.4查看Python路徑

// 系統(tǒng)自帶的python2.7,目錄為/usr/bin/python
which python
//brew安裝的python3.4,目錄為/usr/local/bin/python3
which python3
1.4查看Python路徑.png

1.5使用

// 系統(tǒng)自帶的
python a.py
//brew安裝的
python3 a.py
2.python包管理工具:Pip

一般我們安裝python3時(shí)自帶了Pip箱熬,如果沒有在命令行用HomeBrew安裝:

brew install pip

2.1 配置pipenv:

//安裝 pipenv
pip install pipenv 
//創(chuàng)建virtual env
pipenv --python=python3.6
//安裝依賴
pipenv install
3.Python IDE

這一項(xiàng)不是必須的类垦,我們可以在命令行vi或記事本里寫python代碼。當(dāng)然習(xí)慣使用IDE的童鞋也可以選擇使用IDE城须,筆者這里使用的Python IDE為Pycharm專業(yè)版(自行百度PoJie教程)

4.flask網(wǎng)絡(luò)框架

我們后期理解了區(qū)塊鏈的底層數(shù)據(jù)結(jié)構(gòu)后蚤认,在網(wǎng)絡(luò)層實(shí)現(xiàn)節(jié)點(diǎn)同步和網(wǎng)絡(luò)共識時(shí)會使用到flask網(wǎng)絡(luò)框架。這里糕伐,暫且提一下砰琢,本文內(nèi)容暫時(shí)不會用到。

5.接口調(diào)試工具

同時(shí)后期進(jìn)行區(qū)塊鏈網(wǎng)絡(luò)編程時(shí)還需要一個(gè)HTTP客戶端良瞧,比如Postman陪汽,cURL或其它客戶端。這里我使用的是Postman
)

廢話少說擼代碼

我們知道區(qū)塊鏈?zhǔn)怯幸粋€(gè)個(gè)區(qū)塊構(gòu)成褥蚯,而區(qū)塊內(nèi)又包含了基本的區(qū)塊信息和若干個(gè)交易信息挚冤,一個(gè)交易信息就是對一筆交易的結(jié)構(gòu)封裝(附帶了哈希值等值以防止交易被篡改)。我們今天就從區(qū)塊鏈的最下層數(shù)據(jù)結(jié)構(gòu)交易開始層層分析赞庶,直到構(gòu)成一個(gè)完整的區(qū)塊鏈训挡。

打開Pycharm新建一個(gè)Python項(xiàng)目并新建一個(gè)文件:blockchain.py(使用Vi或記事本開發(fā)的直接新建該文件)

交易類

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類用來封裝一筆交易,并引入哈希加密機(jī)制防止交易數(shù)據(jù)與時(shí)間或者交易鏈被篡改歧强。

#交易鏈的簡單實(shí)現(xiàn)
ChaorsMessage
import datetime  #獲取時(shí)間的庫
import hashlib  #哈希函數(shù)庫
from Transaction import Transaction  #引入交易類

class ChaorsMessage: #交易記錄類

    def __init__(self, data):

        self.data = data   # 交易信息

        self.hash = None  #自身哈希
        self.prev_hash = None  #上一個(gè)交易記錄的哈希
        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()

#密封,相當(dāng)于將交易信息封裝為一個(gè)帶哈希驗(yàn)證值的數(shù)據(jù)結(jié)構(gòu) 使得交易信息(包括交易數(shù)據(jù)和時(shí)間摊册,交易鏈接的順序)不能被修改
    def seal(self): 
        self.hash = self._hash_message()  #對應(yīng)數(shù)據(jù)鎖定

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

        if self.payload_hash != self._hash_payload():
            raise InvalidMessage("交易數(shù)據(jù)與時(shí)間被修改" + 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__':  #單獨(dú)模塊測試
    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()

        #篡改數(shù)據(jù) 篡改數(shù)據(jù)后會捕獲到異常
        # m2.data = "hahahaha"
        # m2.validate()
        #
        m2.prev_hash = "kkkkk"
        # print(m2)
        m2.validate()
    except InvalidMessage as e:
        print(e)

Block類

每個(gè)區(qū)塊包含屬性:索引(index)表悬,Unix時(shí)間戳(timestamp),交易列表(transactions)丧靡,工作量證明(下次講這個(gè)蟆沫,這里暫時(shí)擱置)以及前一個(gè)區(qū)塊的Hash值。

以下是一個(gè)區(qū)塊的結(jié)構(gòu):

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

到這里温治,區(qū)塊鏈的概念就清楚了饭庞,每個(gè)新的區(qū)塊都包含上一個(gè)區(qū)塊的Hash,這是關(guān)鍵的一點(diǎn)熬荆,它保障了區(qū)塊鏈不可變性舟山。如果攻擊者破壞了前面的某個(gè)區(qū)塊,那么后面所有區(qū)塊的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 = []  #存儲多個(gè)交易記錄
       self.timestamp = None  #當(dāng)前時(shí)間戳
       self.hash = None;
       self.prev_hash = None

       #遍歷交易參數(shù)累盗,將所有交易加入到交易列表內(nèi)
       if args:
           for arg in args:
               self.add_message(arg)

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

   def link(self, block):  #鏈接
     #當(dāng)前區(qū)塊的上個(gè)哈希值為上個(gè)區(qū)塊哈希值,將區(qū)塊連接起來
         block.hash = self.prev_hash

   def seal(self):  #區(qū)塊封裝突琳,帶有時(shí)間戳和哈希值的數(shù)據(jù)結(jié)構(gòu)
       self.timestamp = datetime.datetime.now()
       self.hash = self._hash_block()

 #求區(qū)塊的哈希值
   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):  #區(qū)塊合法性驗(yàn)證
       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類用來管理鏈條,它能存儲交易拆融,加入新塊等蠢琳。它就是一個(gè)完整的區(qū)塊鏈

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()  #區(qū)塊封裝
        block.validate()  #區(qū)塊鏈接
        self.blocklist.append(block)

    def validate(self):  #區(qū)塊驗(yàn)證
        for i, block in enumerate(self.blocklist):
            try:
                block.validate()
            except InvalidBlock as e:
                print(e)
                raise InvalidBlockChain("第{}區(qū)塊校驗(yàn)錯誤".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)
        #篡改區(qū)塊
        block3.messagelist[1] = m33
        # m31.data = "lkjioh"
        mychain.validate()

    except InvalidBlockChain as e:
        print(e)

通過上面的代碼,可以對區(qū)塊鏈區(qū)塊的產(chǎn)生和交易有一個(gè)更深刻的了解镜豹。交易和存儲交易的區(qū)塊因?yàn)楣V刀紩哂胁豢纱鄹牡奶匦浴?/p>

當(dāng)然真正的區(qū)塊鏈遠(yuǎn)不止這么簡單傲须,這里只是大概搭建一個(gè)簡單的區(qū)塊鏈Demo,用于理解區(qū)塊鏈的不可篡改原理和基本數(shù)據(jù)結(jié)構(gòu)趟脂。本文代碼也只實(shí)現(xiàn)了區(qū)塊鏈的數(shù)據(jù)層泰讽,至于網(wǎng)絡(luò)層,共識層等昔期,以后有機(jī)會再寫一寫已卸。

源代碼在這里

下一篇:用Python從零開始構(gòu)建區(qū)塊鏈之網(wǎng)絡(luò)+共識(一)

互聯(lián)網(wǎng)顛覆世界,區(qū)塊鏈顛覆互聯(lián)網(wǎng)!

---------------------------------------------------------20180405夜
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末镇眷,一起剝皮案震驚了整個(gè)濱河市咬最,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌欠动,老刑警劉巖永乌,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異具伍,居然都是意外死亡翅雏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門人芽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來望几,“玉大人,你說我怎么就攤上這事萤厅¢夏ǎ” “怎么了靴迫?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長楼誓。 經(jīng)常有香客問我玉锌,道長,這世上最難降的妖魔是什么疟羹? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任主守,我火速辦了婚禮,結(jié)果婚禮上榄融,老公的妹妹穿的比我還像新娘参淫。我一直安慰自己,他們只是感情好愧杯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布涎才。 她就那樣靜靜地躺著,像睡著了一般民效。 火紅的嫁衣襯著肌膚如雪憔维。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天畏邢,我揣著相機(jī)與錄音业扒,去河邊找鬼。 笑死舒萎,一個(gè)胖子當(dāng)著我的面吹牛程储,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播臂寝,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼章鲤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了咆贬?” 一聲冷哼從身側(cè)響起败徊,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎掏缎,沒想到半個(gè)月后皱蹦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡眷蜈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年沪哺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酌儒。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辜妓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情籍滴,我是刑警寧澤酪夷,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站异逐,受9級特大地震影響捶索,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜灰瞻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辅甥。 院中可真熱鬧酝润,春花似錦、人聲如沸璃弄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽夏块。三九已至疏咐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間脐供,已是汗流浹背浑塞。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留政己,地道東北人酌壕。 一個(gè)月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像歇由,于是被迫代替她去往敵國和親卵牍。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容