前言
比特幣在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.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夜