Python从零开始构建区块链之数据层理解

前言

比特币在2017可谓是十足地火爆了,那他背后依赖的区块链技术又是如何实现的呢?当下对区块链人才的需求更是迫切中的迫切,或许在2018将火爆各个行业。

本次系列文章将从实际代码出发,来构建你对区块链技术的认知。

写代码之前

基础技能要求

1.简单的Python基础
2.对HTTP请求有基本的认知
3.面向对象编程思维
4.区块链基本定义

开发环境准备

1.Python3.6

Mac自带的Python为2.7,这里我们需要重新安装Python3.6
1.1确保电脑安装了套件管理工具 Homebrew,如果没有请在命令行执行以下命令安装:

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

1.2 验证是否安装成功,该命令也可以检验电脑是否安装了Homebrew

brew doctor

1.3安装Python3.6

brew install python3

1.4查看Python路径

// 系统自带的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安装:

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夜
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容