Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

用 Python 从零开始创建区块链

江左没浪
308 3 0
我们都对比特币的崛起感到惊讶惊奇,并且想知道其背后的技术——区块链是如何实现的。
" {) w; p2 ?+ F  K% L  }    但是完全搞懂区块链并非易事,至少对我来讲是这样。我喜欢在实践中学习,通过写代码来学习技术会掌握得更牢固。通过构建一个区块链可以加深对区块链的理解。
5 o* N4 w$ s0 g6 z3 \9 I6 p  b! V    准备工作
) t8 u/ J8 X' H3 b! g% B    我们知道区块链是由区块的记录构成的不可变、有序的链结构,记录可以是交易、文件或任何你想要的数据,重要的是它们是通过哈希值(hashes)链接起来的。- k' E4 B+ G3 d  r
    如果你不知道哈希值是什么,这里有一个解释。
/ L3 s2 A6 z' y0 y( E    这份指南的目标人群:阅读这篇文章,要求读者对Python有基本的了解,能编写基本的Python代码,并且需要对HTTP请求有基本的理解。
# @& x/ I  g. Y3 Y  T9 ~    环境准备:确保已经安装Python3.6+,pip,Flask,requests。安装方法:
% d# M3 Q  q7 [; ~4 `& E! j2 D  ?    pipinstallFlask==0.12.2requests==2.18.49 n6 J( f2 _* P: U( @% o+ p0 C
    同时还需要一个HTTP客户端,比如Postman,cURL或其它客户端。, v2 D* V4 \. b; M, b, C$ D' ?: P/ R1 P
    一、开始创建BlockChain
! C3 l* P5 a4 s. ]% ~( x: g    打开你最喜欢的文本编辑器或者IDE,比如PyCharm,新建一个文件blockchain.py,本文所有的代码都写在这一个文件中。如果你丢了它,你总是可以引用源代码。  T; q4 d1 r* |7 C
    BlockChain类
$ @1 y% V% `$ V  D4 I$ i    首先创建一个Blockchain类,在构造函数中创建了两个列表,一个用于储存区块链,一个用于储存交易。
0 f9 {6 \) z6 l9 ^    以下是BlockChain类的框架:
5 Z+ N( q0 g; [1 G3 e, y5 E* S. s  
  1. classBlockchain(object):
    , Y1 u- p( t: V  |
  2.     def__init__(self):$ E  f  r, t. V8 [
  3.     self.chain=[]7 n+ K; |8 W0 N( N# s' E1 k, Y9 r
  4.     self.current_transactions=[]
    5 p9 L, D6 N$ i2 _9 r
  5.     defnew_block(self):
    , K! T5 ]/ I$ [4 K
  6.     #CreatesanewBlockandaddsittothechain
    * ]% o& [, m2 r, N5 v
  7.     pass6 x+ w5 f! n5 n# j
  8.     defnew_transaction(self):: [' n, a4 ?6 I) b/ J9 ]% h
  9.     #Addsanewtransactiontothelistoftransactions" _+ e& U# ~; l0 v0 m( X! y
  10.     pass% J+ {% a) l  O3 \  X8 \1 h
  11.     @staticmethod
    * j2 Q0 g* [6 W- ^. k( C$ m3 x1 ~
  12.     defhash(block):
    % S; E2 H" H7 y+ ^0 _. Z- Z, _
  13.     #HashesaBlock/ J  N5 M, f' q- c
  14.     pass/ i+ G" C* M( f) h+ o
  15.     @property" C. ?# X9 A2 r' W% F9 v" \$ K
  16.     deflast_block(self):! |. `; _. R5 q! f' d+ a& g1 j3 m
  17.     #ReturnsthelastBlockinthechain, R  d! y' o5 E& U  q
  18.     pass
复制代码
. l0 R$ u0 i2 i/ Z8 {2 f
    -我们的区块链类的蓝图-, O& ^. c8 ]2 d' z, T, N
    Blockchain类用来管理链条,它能存储交易,加入新块等,下面我们来进一步完善这些方法。. U  J# ~) v. G+ M+ z& B, @5 |
    块结构+ y% E7 _! n$ X1 p/ _% o4 A
    每个区块包含属性:索引(index),Unix时间戳(timestamp),交易列表(transactions),工作量证明(稍后解释)以及前一个区块的Hash值。, e4 o& `8 U+ `, d4 S
    以下是一个区块结构:5 z! }% c+ ]/ W) \1 b, y; U
   
  1. block={$ `4 ~" X, }4 U' k
  2.     'index':1,: G* A2 w& \4 {* r+ R
  3.     'timestamp':1506057125.900785,
      `) J% Y" {9 V  z0 ?
  4.     'transactions':[1 |0 }) \: F+ x7 \: Y( w& ], G
  5.     {
    * ^9 p, B4 q5 ^( I# Y' J9 D5 x: M
  6.     'sender':"8527147fe1f5426f9dd545de4b27ee00",
    ' U" _5 N9 A& t8 r# j
  7.     'recipient':"a77f5cdfa2934df3954a5c7c7da5df1f",  D$ w+ w) x) G8 s( g
  8.     'amount':5,
    ) G+ j, V$ \6 m; a( I6 \
  9.     }
    6 Z# `  \2 `# ^6 ?
  10.     ],
      |% _& J' f! g$ b3 G
  11.     'proof':324984774000,. L; t1 J0 X2 w% J& g- b
  12.     'previous_hash':"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
    % _) w  D/ m( p5 }, a1 r4 }# `
  13.     }
复制代码

9 B: Y- c2 \* a    -链上一个区块的例子-
' ~1 ]; C7 ?/ a- L    到这里,区块链的概念就清楚了,每个新的区块都包含上一个区块的Hash,这是关键的一点,它保障了区块链不可变性。如果攻击者破坏了前面的某个区块,那么后面所有区块的Hash都会变得不正确。! s# i# {/ ^' R1 w
    确定这有用吗?嗯,你可以花点彻底了解它——这可是区块链背后的核心理念。0 G4 M; \5 s* C) ^1 C
    加入交易. D+ g% x) X) d# a: T
    接下来我们需要添加一个交易,来完善下new_transaction()方法。+ @: v  W2 ?% g4 y. M% G
  
  1. classBlockchain(object):9 i) C1 @3 W( E' l
  2.     ...
    + {* T0 S3 h- }; D
  3.     defnew_transaction(self,sender,recipient,amount):( Q6 P% Z5 N8 q! R) q6 b8 {1 v
  4.     """! K& h, z! D3 l2 m4 v0 \7 \
  5.     生成新交易信息,信息将加入到下一个待挖的区块中
    ; h! g/ g) s0 K3 ~; J7 p" _1 h+ M
  6.     :paramsender:AddressoftheSender9 A7 ?' B8 x' l# t7 q  j
  7.     :paramrecipient:AddressoftheRecipient; y& z( d; N1 |
  8.     :paramamount:Amount, |* D, ]# W# L0 _. q: v
  9.     :return:TheindexoftheBlockthatwillholdthistransaction
    4 U0 s- b8 \) `: J2 W5 c
  10.     """
    6 E1 p8 v7 E& o' x; ?
  11.     self.current_transactions.append({$ j+ C! k' R  n4 K7 u3 b
  12.     'sender':sender,
    6 F3 _5 C& d+ y# `
  13.     'recipient':recipient,3 r) d5 m3 f9 B" H7 f8 [
  14.     'amount':amount,7 G& F8 W; x0 _# p2 s& i# h
  15.     })
    . {: e3 z: V/ v9 f' `! c0 P
  16.     returnself.last_block['index']+1
复制代码
) ~4 V! ^3 s5 s1 s7 p
    new_transaction()方法向列表中添加一个交易记录,并返回该记录将被添加到的区块(下一个待挖掘的区块)的索引,等下在用户提交交易时会有用。" s% J; ~  k0 v8 F3 F
    创建区块
  |1 s8 i2 ?/ i    当Blockchain实例化后,我们需要构造一个创世块(没有前区块的第一个区块),并且给它加上一个“工作量证明”。
! X: f/ F; {" V" y    每个区块都需要经过工作量证明,俗称挖矿,稍后会继续讲解。6 h& y  _0 B: u
    为了构造创世块,我们还需要完善new_block()、new_transaction()和hash()方法:
+ j3 |1 S' R4 `/ w+ u  
  1. importhashlib# w5 s4 ~. j4 C6 T$ v% h/ y
  2.     importjson5 `8 g- V- g9 g% o, [: Z
  3.     fromtimeimporttime* J7 P) t# _" y- ^# p3 d8 m
  4.     classBlockchain(object):' T. }1 D! \+ j0 \
  5.     def__init__(self):
    0 _" |! |3 t" E+ d
  6.     self.current_transactions=[]7 q) }) |8 h! A- L8 x' i7 t
  7.     self.chain=[]
    5 I  B8 p+ g" x, H- P# D
  8.     #Createthegenesisblock5 P, p* ^, B) X" C
  9.     self.new_block(previous_hash=1,proof=100)) G- @- Q- g9 ^9 i
  10.     defnew_block(self,proof,previous_hash=None):* k6 {  @; V5 _
  11.     """) m8 Z* e- i' V3 ?! A* G6 s
  12.     生成新块6 p) a7 C0 F, \
  13.     :paramproof:TheproofgivenbytheProofofWorkalgorithm3 @* H# ^5 \2 d5 D0 b' L& ]4 z1 R% `
  14.     :paramprevious_hash:(Optional)HashofpreviousBlock- a3 Z' H- e$ T7 k& m7 ~+ l1 u
  15.     :return:NewBlock
    ) Y8 _. o% d( A8 V! ?
  16.     """1 z- ]+ O$ X/ C( F4 I) t
  17.     block={
    - J' O0 p+ e" ^, }6 `7 q) X# ~
  18.     'index':len(self.chain)+1,
    5 I" i3 E( }" P! x1 |3 m. m
  19.     'timestamp':time(),) w. q) S  o- \
  20.     'transactions':self.current_transactions,
    5 N' S* Z+ ^, z, Y
  21.     'proof':proof,
    ) z) a; d8 F' |; c4 e7 u, _
  22.     'previous_hash':previous_hashorself.hash(self.chain[-1]),
      t4 l1 s5 l' b% |& t
  23.     }
    7 x5 J- m& O' ~4 o
  24.     #Resetthecurrentlistoftransactions" l; y/ A4 f8 D
  25.     self.current_transactions=[]
    7 R3 U0 [# ~- A+ \
  26.     self.chain.append(block)
    ( }( ]+ k! R+ c1 P) S
  27.     returnblock% M( K, Q4 h( A
  28.     defnew_transaction(self,sender,recipient,amount):; a: ~( Q: y: o7 D
  29.     """
    6 p0 @# ]$ R* F# E7 J0 U8 Y- }' N/ t
  30.     生成新的交易信息,信息将加入到下一个待挖的区块中! P3 `( H1 r5 J! f% {/ j% ?
  31.     :paramsender:AddressoftheSender3 o# D; I2 |  |5 s8 \
  32.     :paramrecipient:AddressoftheRecipient
    * l( K3 M# r; z9 {# F  D
  33.     :paramamount:Amount3 H% f+ m0 q& h- U- w
  34.     :return:TheindexoftheBlockthatwillholdthistransaction
    ) E- U( k# x" P3 z
  35.     """& |. x6 ]' W6 O, z1 Y* M8 @( }& W
  36.     self.current_transactions.append({3 {& T& J; E9 g* l2 d' C, B
  37.     'sender':sender,
    3 }! w( D0 o2 Y" {: N) c7 m
  38.     'recipient':recipient," ?6 o/ c, G4 N* u$ o" ^
  39.     'amount':amount,
    3 I9 p5 |. p& J
  40.     })
    1 V# k7 @4 o- o9 D* `8 P; `
  41.     returnself.last_block['index']+17 J! n2 C* k3 k0 n4 d7 N; s
  42.     @property
    - _( [+ A( B' h
  43.     deflast_block(self):
    ' a; f. ~! t# y9 z& g
  44.     returnself.chain[-1]8 |  I, x2 W1 X8 r0 R
  45.     @staticmethod/ }7 }3 u3 @9 f* T9 P
  46.     defhash(block):
    ; {5 [1 r' W: `# K
  47.     """
    " n9 V# h' F3 P! o% F. {# O* F8 ]
  48.     生成块的SHA-256hash值, J1 b2 Q! I. r, Q
  49.     :paramblock:Block
    ( {, x! i5 ]- z/ S/ E
  50.     :return:5 }1 u3 v' g3 {, w" G$ ]8 J5 Y
  51.     """
复制代码

0 ]% H; J% c* v0 P    #WemustmakesurethattheDictionaryisOrdered,orwe'llhaveinconsistenthashes
) o0 q( T- z8 A4 {3 Z! F    block_string=json.dumps(block,sort_keys=True).encode()+ c* W" h* f! {4 R2 `% j
    returnhashlib.sha256(block_string).hexdigest()2 C- ]2 e1 R4 A. p) u
    上述应该是非常直接的——我已经添加了一些注释和字符来帮助大家理解。当表达出我们的区块链时,我们也快要完成了。但现在,你肯定在疑惑新区块是如何被创建、伪造或是挖出的。" E% \6 P0 V5 X' k1 g
    理解工作量证明
: O0 C! M/ g+ S3 S; S) d& p7 `  g    新的区块依赖工作量证明算法(PoW)来构造。PoW的目标是找出一个符合特定条件的数字,这个数字很难计算出来,但容易验证。这就是工作量证明的核心思想。
7 K1 I$ G+ T* O    为了方便理解,我们举个例子:
6 {( X7 }, a+ i5 I4 @/ J; q- v    假设一个整数x乘以另一个整数y的积的Hash值必须以0结尾,即hash(x*y)=ac23dc…0。设变量x=5,求y的值?用Python实现如下:& B4 b! E2 c  j1 p  S/ n
  1.   fromhashlibimportsha2560 D) H0 k, X; Z( v# s" _! L
  2.     x=5
    $ I% [) D+ R0 k% k$ i" C1 ~  h2 B7 _
  3.     y=0#y未知, Y& u! z2 l- U" L. L
  4.     whilesha256(f'{x*y}'.encode()).hexdigest()[-1]!="0":
    ( o- W' v# N7 W0 d
  5.     y+=14 T" i: b7 \9 H8 L
  6.     print(f'Thesolutionisy={y}')
复制代码

( _' X1 w5 J6 h+ Y2 F. [/ h    结果y=21,因为:. v4 C) k* @+ a; E4 F
    hash(5*21)=1253e9373e...5e3600155e860
4 n( O& u$ O+ G& g5 {    在比特币中,使用称为Hashcash的工作量证明算法,它和上面的问题很类似。矿工们为了争夺创建区块的权利而争相计算结果。通常,计算难度与目标字符串需要满足的特定字符的数量成正比,矿工算出结果后,会获得比特币奖励。
" P  J5 v9 I) m$ H, n% ~" L5 F- i    当然,在网络上非常容易验证这个结果。
3 g+ n% z0 f/ e$ k9 I    实现工作量证明
" G% L& ^! l( l  b4 H* T    让我们来实现一个相似PoW算法,规则是:1 y7 ^  L6 i' m+ e* D+ L" O
    寻找一个数p,使得它与前一个区块的proof拼接成的字符串的Hash值以4个零开头。4 Y0 q  U# y9 n  L$ k
  
  1. importhashlib
    ( b* j8 N& w. |8 F  a, X* v
  2.     importjson0 `% Z. c/ V% ~3 Q+ w
  3.     fromtimeimporttime  l; l; N4 _) q9 l! U: B
  4.     fromuuidimportuuid4* x( j; H: F- n: `! ?' `, t- H
  5.     classBlockchain(object):
    ! v0 D0 j# b- v4 w- }( [
  6.     ...
    ) h5 E0 `2 @# W5 Z# v1 i
  7.     defproof_of_work(self,last_proof):
    2 X% Q- y  _# Y6 M) b
  8.     """1 R% u9 Q5 V( [0 \  l
  9.     简单的工作量证明:
    & Y! U/ @8 y" i0 t
  10.     -查找一个p'使得hash(pp')以4个0开头2 U3 o  U' P1 \1 O: d3 G- O
  11.     -p是上一个块的证明,p'是当前的证明
    & D( k: ]8 F% P
  12.     :paramlast_proof:
    3 t- e+ J6 a" z1 P
  13.     :return:
    ( G! e; Y  W' N* K3 Y
  14.     """
    ; n  x+ g& ]$ G0 R- s9 m3 G# G# q
  15.     proof=07 _2 _: z7 [3 Q" X- ?
  16.     whileself.valid_proof(last_proof,proof)isFalse:. |( E2 v# `3 ]; a
  17.     proof+=1  g  g5 c1 W  S" ~
  18.     returnproof% M  z8 o& o- e) l# p
  19.     @staticmethod
    , c! w6 v; b8 ?- J3 S, D
  20.     defvalid_proof(last_proof,proof):% `* c5 Z( k0 o5 M6 v$ z  E! G
  21.     """2 W9 W' w% J# s, G" d8 j7 i
  22.     验证证明:是否hash(last_proof,proof)以4个0开头?! F. p! q, I  f; i
  23.     :paramlast_proof:PreviousProof+ x$ u, K1 C/ ]  Y4 H
  24.     :paramproof:CurrentProof
    4 z/ V# K# O) d! x2 o' V/ Y3 Y# \0 r
  25.     :return:Trueifcorrect,Falseifnot.! P) X3 u- G3 D& w3 b$ q
  26.     """
    2 B: H0 i" C* p1 k# N* X7 b( q& q% `
  27.     guess=f'{last_proof}{proof}'.encode()
    , P& H  _% C% ]5 ~" _4 N
  28.     guess_hash=hashlib.sha256(guess).hexdigest()
    ' {) V# b, w# k$ J& U8 Q' S
  29.     returnguess_hash[:4]=="0000"
    ' O" ]. J. Q( G! E
  30.     ```
复制代码

6 `) T7 U9 @9 A% X    衡量算法复杂度的办法是修改零开头的个数。使用4个来用于演示,你会发现多一个零都会大大增加计算出结果所需的时间。3 D& A2 ^* K8 h/ U2 ^# J
    现在Blockchain类基本已经完成了,接下来使用HTTPrequests来进行交互。5 ~; k4 f8 m. `, \& {
    ##二、BlockChain作为API接口+ T( i7 q1 @4 }5 t1 g) Y3 s
    我们将使用PythonFlask框架,这是一个轻量Web应用框架,它方便将网络请求映射到Python函数,现在我们来让Blockchain运行在基于Flaskweb上。$ y" W; B+ V; p% A' j3 J( z
    我们将创建三个接口:
' u% \$ i7 F$ N7 S1 r: a+ Y   
  1. ```/transactions/new```创建一个交易并添加到区块  b9 k7 d& f- x, z( |
  2.     ```/mine```告诉服务器去挖掘新的区块" B7 d  ^( k  r5 Z6 d
  3.     ```/chain```返回整个区块链7 `5 X5 B/ K0 F$ c* x+ _8 L
  4.     ###创建节点
    , E& Z4 `, ]% ]2 O5 s* F4 Y( l
  5.     我们的Flask服务器将扮演区块链网络中的一个节点。我们先添加一些框架代码:# ?5 W; s  f# t& ~2 r
  6.     importhashlib
    ; B, }- c2 q+ `- H' \# q( S4 `* y) a
  7.     importjson
    9 w: L5 l( H) v: T. W0 W
  8.     fromtextwrapimportdedent1 T* F, p, R2 j! d: C4 y
  9.     fromtimeimporttime6 l9 T, V' S+ T0 k" m" o2 R% X# F
  10.     fromuuidimportuuid4. y1 e. o! ~- Q* Y
  11.     fromflaskimportFlask
    4 l3 k% D/ F& M* M( V1 O6 @( a! e
  12.     classBlockchain(object):
    - C. ^* F8 x* B- J
  13.     …; o5 t: d  y+ Z) }; q
  14.     InstantiateourNode
    8 u, K* ^% P1 i* {" b  e5 z2 B
  15.     app=Flask(name)4 e5 j0 J5 K. r8 {' J/ A
  16.     Generateagloballyuniqueaddressforthisnode$ w; N  G$ E# E9 Y4 W, g4 J& j
  17.     node_identifier=str(uuid4()).replace(’-’,‘’)$ [* F! p4 W  ~
  18.     InstantiatetheBlockchain7 Y) A. w0 B6 ~
  19.     blockchain=Blockchain()- ~% a% m6 b4 T0 {; I1 |+ E
  20.     @app.route(’/mine’,methods=[‘GET’])
    - U3 u" b7 }1 C; ?/ c3 m
  21.     defmine():' A) j- J/ m8 r) L+ R5 d+ ?
  22.     return“We’llmineanewBlock”
    # I) D* `" A6 w& A/ P
  23.     @app.route(’/transactions/new’,methods=[‘POST’])  |+ m: h' D; D! e# f
  24.     defnew_transaction():
    1 \3 C" ^) O2 t  k7 R) Z: ]
  25.     return“We’lladdanewtransaction”( u( p; G6 o4 f
  26.     @app.route(’/chain’,methods=[‘GET’])0 R. U/ _8 z9 e; ]
  27.     deffull_chain():( M2 Q0 k2 ], U
  28.     response={
    2 N' p) k5 S/ Y. Z0 a
  29.     ‘chain’:blockchain.chain,4 S) H& W( ~5 E% {  p: `' O' t
  30.     ‘length’:len(blockchain.chain),2 h- j; }/ ]* \( M. a: Z$ C
  31.     }
    6 n5 U; k% U/ e/ O5 l1 h
  32.     returnjsonify(response),200' W0 P  D4 V& J# b2 Z, O/ L3 e
  33.     ifname==‘main’:
    8 K/ H* B0 P4 d; s8 K! T
  34.     app.run(host=‘0.0.0.0’,port=5000)
    - Q; `. @' q, b% I
  35.     ```
    * p! o0 U8 x7 x; j- q
  36.     简单的说明一下以上代码:
    4 k, |& m' _( t$ H1 r" p
  37.     第15行:创建一个节点。在这里阅读更多关于Flask的东西。$ t. W8 T' ^) T) ]
  38.     第18行:为节点创建一个随机的名字。
    9 Q6 _# V' `" |: U
  39.     第21行:实例Blockchain类。. [) Q/ E' s; Q5 W8 k$ [
  40.     第24–26行:创建/mineGET接口。
    * q: \* i2 }2 t; F. `
  41.     第28–30行:创建/transactions/newPOST接口,可以给接口发送交易数据。, @& \3 `2 A) a( r, w$ m5 {$ l9 a
  42.     第32–38行:创建/chain接口,返回整个区块链。
    , L* |: ~( o1 c+ I) D) d4 `4 H
  43.     第40–41行:服务运行在端口5000上。
    9 a4 B3 R$ T; I) Z* j, f( \. u
  44.     发送交易8 s, J& G  {% q2 E$ i1 m
  45.     发送到节点的交易数据结构如下:
    8 M; c2 o4 H6 G0 G& `& o
  46.     {4 v) ~! A% p3 p  p! H/ u1 _
  47.     "sender":"myaddress",+ [. a6 F* k; ]/ _' \
  48.     "recipient":"someoneelse'saddress",
    9 N& Z: i4 a  ~
  49.     "amount":50 l+ i) G  k( v+ @5 n
  50.     }9 \/ N& t5 Y% ]! O2 k  g; {
  51.     之前已经有添加交易的方法,基于接口来添加交易就很简单了:" s; l. N, u+ Y3 ~) ^- }
  52.     importhashlib" L. u. k" E2 s+ s' ]( G% `
  53.     importjson
      a8 N9 c" d/ q  T; a5 ^" D# Q1 \
  54.     fromtextwrapimportdedent
    / Q& V9 Q+ S+ q4 _2 u& H
  55.     fromtimeimporttime
    8 Z9 x5 F' G) c% D( B7 o6 T/ H4 A# _9 h
  56.     fromuuidimportuuid4
    ; h% W7 {) h5 ^4 U7 E3 c
  57.     fromflaskimportFlask,jsonify,request5 A+ d4 ~. T# F, p+ w) t8 m  n" C9 H. l
  58.     ...  x( P+ b  F. @. P7 \  [
  59.     @app.route('/transactions/new',methods=['POST'])
    2 W: X: m+ F/ l0 k9 X
  60.     defnew_transaction():
    2 {$ q* i! |7 {5 @' _' q
  61.     values=request.get_json()
    + f. a0 K7 }  s: n4 e# E7 I$ M( t% y
  62.     #CheckthattherequiredfieldsareinthePOST'eddata
    0 c; i3 x$ W7 B9 j: h7 Z3 I
  63.     required=['sender','recipient','amount']
    $ v0 f/ u! Y- h1 x% ^2 t
  64.     ifnotall(kinvaluesforkinrequired):* z9 s) q: v. e7 n* K: d
  65.     return'Missingvalues',4004 q# g) K% ]/ m; V! Z
  66.     #CreateanewTransaction+ Q2 g% \+ P. [4 w, v4 {; C6 E& C
  67.     index=blockchain.new_transaction(values['sender'],values['recipient'],values['amount'])
    4 N: R" {* |; n# H  r* b
  68.     response={'message':f'TransactionwillbeaddedtoBlock{index}'}2 \( Q7 D! U  d& y# D5 Y! [$ n
  69.     returnjsonify(response),201
    9 V3 L' o" ^" f! k. |
  70.     ```
    " |9 ^* C9 Z  ]' [$ v3 K" U9 O0 Y3 m
  71.     -创建交易的方法-
    0 E' J0 a7 l8 p+ `3 V
  72.     ###挖矿5 |! A+ `% p% Q- f
  73.     挖矿正是神奇所在,它很简单,做了一下三件事:0 V  A2 H7 B0 C$ n
  74.     计算工作量证明PoW
    3 F3 u, D4 e$ O) l3 }& ]1 L+ c/ e. C
  75.     通过新增一个交易授予矿工(自己)一个币* _) k6 L5 ?% ~& F
  76.     构造新区块并将其添加到链中7 M4 H6 ]/ z+ H3 F6 `# G
  77.     importhashlib
    + [1 ^9 Z2 N( G7 N7 X
  78.     importjson6 e7 |- Q( k  `0 F! m: d
  79.     fromtimeimporttime
    ! P1 ~9 M7 I# M' W
  80.     fromuuidimportuuid4% h/ N  G% D' L# X$ {; _# F
  81.     fromflaskimportFlask,jsonify,request
    " ?0 H% }+ s+ B# v8 X: W, h
  82.     …
    8 b; {. e  a; i7 w( g( E$ M
  83.     @app.route(’/mine’,methods=[‘GET’])& K" }% r3 ^! O$ F4 W$ d9 A0 Q1 F
  84.     defmine():
    % Q2 ^# Y. v, I! Z, n6 B& I
  85.     #Weruntheproofofworkalgorithmtogetthenextproof…
    & a9 d; O) h: R
  86.     last_block=blockchain.last_block
    : G2 [# J4 A' t
  87.     last_proof=last_block[‘proof’]
    % L2 a' H# i+ E. ^0 o0 k) B6 a
  88.     proof=blockchain.proof_of_work(last_proof)# ?+ x( ^4 r" v- K+ {" p' [
  89.     #给工作量证明的节点提供奖励.% Q/ M. H- l, b( D
  90.     #发送者为"0"表明是新挖出的币.
    $ i1 @* p; P' v
  91.     blockchain.new_transaction(
    + N  ?& t% P( Y: X' b9 x1 R, M; `
  92.     sender="0",  _: L% w- r2 M" k5 b/ [
  93.     recipient=node_identifier,' ]& [+ |' w- a7 F
  94.     amount=1,
    % L3 @# O6 |1 N5 l+ r3 R
  95.     )) u( d$ J1 x5 U9 Q5 g/ |$ m, Q
  96.     #ForgethenewBlockbyaddingittothechain
    ; {: w/ G0 }) i4 Z) Q- k
  97.     previous_hash=blockchain.hash(last_block)
    ) g( O' y% p+ X, D8 J  x
  98.     block=blockchain.new_block(proof,previous_hash)' d! ?. `5 f  X, F/ K
  99.     response={3 b  p" D9 i( x- D/ E2 j* q* a
  100.     'message':"NewBlockForged",
    " H( G4 r3 P$ f5 J5 J8 V/ p4 h
  101.     'index':block['index'],
    6 S* l! G) u& r$ d, a. X. e
  102.     'transactions':block['transactions'],
    % ^& J1 A1 L  M- o
  103.     'proof':block['proof'],
    4 p! t  ~: O7 U7 H5 R
  104.     'previous_hash':block['previous_hash'],
    1 c* R  K9 B, J1 N
  105.     }" m( {8 G0 E. F
  106.     returnjsonify(response),2001 V" U1 ^2 ~0 d+ H" |! s+ T
  107.     ```
复制代码
& w& Y/ k2 ?) W! u& s
    注意交易的接收者是我们自己的服务器节点,我们做的大部分工作都只是围绕Blockchain类方法进行交互。到此,我们的区块链就算完成了,我们来实际运行下。* j: Z& E1 u$ M  c0 n1 B" A( \
    三、运行区块链
7 W) m# ~* \$ q$ X# X2 A    你可以使用cURL或Postman去和API进行交互* x+ w7 m/ F. S: r
    启动server:
5 P! _1 l" ~5 Q% b1 ^    $pythonblockchain.py
9 Y, J' ~1 Q$ H6 O, P    *Runningonhttp://127.0.0.1:5000/(PressCTRL+Ctoquit)
* D4 r+ v" I  y9 ^+ D    让我们通过发送GET请求到http://localhost:5000/mine来进行挖矿:
" ~$ I8 t' L/ R8 Q' O    -使用Postman以创建一个GET请求-' C( @* b/ d9 }1 c6 i
    通过发送POST请求到http://localhost:5000/transactions/new,添加一个新交易:
: H0 f# Q  [5 G! R: t    -使用Postman以创建一个POST请求-* n; W  N' R( u: Y/ |* H
    如果不是使用Postman,则用一下的cURL语句也是一样的:
+ @1 L& Z' E! D    $curl-XPOST-H"Content-Type:application/json"-d'{  B8 C; x8 Z2 T/ n( D7 e
    "sender":"d4ee26eee15148ee92c6cd394edd974e",3 N$ H& }: ~; x  ?! [
    "recipient":"someone-other-address",4 Q9 ^3 L8 Q; M2 F' c
    "amount":5
( F) Q! w% _, a; l$ l+ D- ?    }'"http://localhost:5000/transactions/new"; H. K" ?, d7 b9 Y& M  i8 h
    在挖了两次矿之后,就有3个块了,通过请求http://localhost:5000/chain可以得到所有的块信息:: @% A2 u) D+ r9 D1 f2 ^. i
   
  1. {# [3 q" W, P, U
  2.     "chain":[
    9 j4 S3 A2 q. S4 {6 T+ p  K
  3.     {+ n) o7 k7 ]" D' L- K4 t
  4.     "index":1,
    4 }& z# N/ s, q) y1 M
  5.     "previous_hash":1,
    & o0 C+ Z! a, s6 ~0 D. j- I
  6.     "proof":100,
    3 @1 v3 L" T, T, M
  7.     "timestamp":1506280650.770839,# y( A( d3 p) X3 x5 `# i: h. P
  8.     "transactions":[]
    4 z+ t$ S' e$ V) v1 d
  9.     },- z/ D6 p- p; Y* V' _
  10.     {
    3 i4 v; G% F9 H+ F1 W
  11.     "index":2,
    ; B# I* t( G. I/ q; }, C
  12.     "previous_hash":"c099bc...bfb7",
    1 F; i; o0 g1 A% @
  13.     "proof":35293,
    : {3 _6 I+ o3 v% L! `
  14.     "timestamp":1506280664.717925,
    4 S/ C# X  z- f% _7 t
  15.     "transactions":[
    ) F/ j0 g' c% K
  16.     {# B. `3 s6 |1 S$ A4 R: r
  17.     "amount":1,
    ; u3 v9 v) x. b- m6 J/ Y/ n
  18.     "recipient":"8bbcb347e0634905b0cac7955bae152b",
    9 v7 i% g  B  A
  19.     "sender":"0"( b8 S& F  A! A  [
  20.     }% S, p" x# ?0 B0 y
  21.     ]
    " }, H  @" _9 H
  22.     },0 v- v+ [' p" @( j  j+ o
  23.     {- L1 g' `. p$ u0 A9 V
  24.     "index":3,: Z6 s5 w$ e, Z8 {
  25.     "previous_hash":"eff91a...10f2",  h4 p3 |4 u7 ^, ^) }3 d; x
  26.     "proof":35089,4 a8 A+ Z* ^: F+ F. s
  27.     "timestamp":1506280666.1086972,$ z7 w- R( \) c& [2 x9 ^/ l
  28.     "transactions":[
    $ z. k4 V* R/ {; M1 ?) ^  `
  29.     {
    ! p' ]0 m$ R$ E! E
  30.     "amount":1,
    " L& U4 P2 U1 ~( G1 T: L8 ]
  31.     "recipient":"8bbcb347e0634905b0cac7955bae152b",  |  J9 e: i0 }9 }1 U+ c2 v
  32.     "sender":"0"
    - Z6 k5 I6 d1 T& X% c0 V
  33.     }
    5 L) p, m  n0 }
  34.     ]4 M! i. P; t4 m1 j2 L/ ^/ W
  35.     }$ W+ \# R2 i5 P: W
  36.     ],7 w! C4 H# c% N* d1 V( u1 B4 D
  37.     "length":3
    , c/ L1 F* X6 @0 R3 ^3 }
  38.     }
复制代码

) {! z7 c4 k5 U; G# V" B    四、一致性(共识)2 F0 J' H! y; K' }* G
    非常棒,我们已经有了一个基本的区块链可以接受交易和挖矿。但是区块链系统应该是分布式的。既然是分布式的,那么我们究竟拿什么保证所有节点有同样的链呢?这就是一致性问题,我们要想在网络上有多个节点,就必须实现一个一致性的算法。
" N$ [- `6 q, ]! {    注册节点# N5 Y$ _/ U4 S% n) u
    在实现一致性算法之前,我们需要找到一种方式让一个节点知道它相邻的节点。每个节点都需要保存一份包含网络中其它节点的记录。因此让我们新增几个接口:+ Y$ \5 P, G7 p+ Q0 M- L: y
    /nodes/register接收URL形式的新节点列表
3 p7 t. p7 a4 H; m9 @& u    /nodes/resolve执行一致性算法,解决任何冲突,确保节点拥有正确的链( D7 x. z! C4 [2 O7 M/ q
    我们修改下Blockchain的init函数并提供一个注册节点方法:2 X/ u! R. }' Z! p4 y  ~* c+ {
  
  1. ...; y5 P1 j2 |  k* G
  2.     fromurllib.parseimporturlparse
    1 l. M" E9 V% l2 W  W! `0 ?
  3.     ...! \, F7 Q4 l1 T) A* Y$ `
  4.     classBlockchain(object):
    9 }; l& w; }, C  y: U- k
  5.     def__init__(self):9 ~" O( p$ j  W9 O2 J: A) T
  6.     ...
    7 t* G! _; w2 m* }) \- ^* N
  7.     self.nodes=set()5 X6 s' ~$ `: p* A$ e" A
  8.     ...
    ; s0 ]( U2 \/ _  e) R4 k  F
  9.     defregister_node(self,address):
    + _" m+ r% `6 m
  10.     """
    5 y& B1 C; @5 Y1 O$ s
  11.     Addanewnodetothelistofnodes
    * a* o% l- H6 Z) B% m1 Y. V- n/ _
  12.     :paramaddress:Addressofnode.Eg.'http://192.168.0.5:5000'3 Z! c7 M0 E' N0 [% k- @
  13.     :return:None
    - g9 N! S7 P* f) b* b, F' |
  14.     """9 |  H& s4 J, i0 I0 |
  15.     parsed_url=urlparse(address)
    - S' s4 U+ j. f! ]. [8 z5 {) K
  16.     self.nodes.add(parsed_url.netloc)9 v* ^" Y# @% X. Y% J4 ?5 M
  17.     ```+ \' j: M2 ^4 ]% ?
  18.     我们用set()来储存节点,这是一种避免重复添加节点的简单方法——这意味着无论我们添加特定节点多少次,它实际上都只添加了一次。
    ; ^: n. B. T+ C/ V% z! j' O3 O
  19.     ###实现共识算法* d- k' D" O* k1 G5 x1 E6 X
  20.     前面提到,冲突是指不同的节点拥有不同的链,为了解决这个问题,规定最长的、有效的链才是最终的链,换句话说,网络中有效最长链才是实际的链。我们使用以下的算法,来达到网络中的共识。
    9 _* V( T, G! ?0 b8 X
  21.     …1 W: g! |: c4 T2 E1 X& P! W: m
  22.     importrequests
    0 D- l  v2 x- B7 a
  23.     classBlockchain(object)+ i" i2 h9 q' G8 J' {/ a
  24.     …
    ; N1 q9 v/ B$ Q" j2 K
  25.     defvalid_chain(self,chain):% {' {$ p, S+ g" J, D
  26.     """
    1 B! Z8 \6 `# S8 @. K
  27.     Determineifagivenblockchainisvalid
    9 H- Y" E5 f7 n& L3 L
  28.     :paramchain:Ablockchain
      l! @7 R( d2 z3 K
  29.     :return:Trueifvalid,Falseifnot2 p. q7 X- O5 C7 b' \7 A! z* D7 _
  30.     """
    ' j/ Q6 G  e  l+ \
  31.     last_block=chain[0]
    7 W8 \: ~; m  U4 T/ L
  32.     current_index=1
    8 [, p( W5 w; z
  33.     whilecurrent_indexmax_lengthandself.valid_chain(chain):9 N. O  q7 R( @8 B+ x% k7 x5 t) F
  34.     max_length=length+ r! h; G* i( u
  35.     new_chain=chain  {! t# t' T+ ~
  36.     #Replaceourchainifwediscoveredanew,validchainlongerthanours, P$ Y. x3 Z) B# ^5 y9 g5 [
  37.     ifnew_chain:
    $ k4 s$ S& j  F$ z* S
  38.     self.chain=new_chain
    * ?7 w- O9 z( p$ T1 N3 Q
  39.     returnTrue4 o, J/ A8 f2 {+ s9 z, K
  40.     returnFalse
    9 ?# b* ^$ r$ `! M% o
  41.     ```
复制代码

6 ~) W) v0 i- p+ ~    第1个方法valid_chain()用来检查是否是有效链,遍历每个块验证hash和proof。
& W1 ]) S# J3 G+ C$ E    第2个方法resolve_conflicts()用来解决冲突,遍历所有的邻居节点,并用上一个方法检查链的有效性,如果发现有效更长链,就替换掉自己的链。
# ?6 V4 Y  [* X: |3 D) O    让我们添加两个路由,一个用来注册节点,一个用来解决冲突。
) B6 w4 V+ {  r, ]* @$ L. D5 i   
  1. @app.route('/nodes/register',methods=['POST'])
    / p6 K3 K- e' t8 G
  2.     defregister_nodes():5 J$ \) ~! I8 L3 l2 z- C
  3.     values=request.get_json(): C& L" x- j, |# N# X; g- X; ?
  4.     nodes=values.get('nodes')) j9 D" f, {' L) u3 x
  5.     ifnodesisNone:9 u4 S: z  x& X. D, _/ j& r6 O0 }
  6.     return"Error:Pleasesupplyavalidlistofnodes",400( \7 [9 g5 P+ c5 J" M  i, y
  7.     fornodeinnodes:' t6 v) e8 _; P: S5 i9 E
  8.     blockchain.register_node(node)" \7 Z- i' n2 H  r
  9.     response={8 D, F) L; k+ R! q! _
  10.     'message':'Newnodeshavebeenadded',
    8 }2 A1 C1 |' f3 m
  11.     'total_nodes':list(blockchain.nodes),
    + P& ^/ ?% D8 c' F- H1 A2 P0 t9 R
  12.     }
    # R. Z; V" W! E8 v
  13.     returnjsonify(response),201: Q. Z4 c& _$ _$ Z* @6 ^# K0 g
  14.     @app.route('/nodes/resolve',methods=['GET'])
    7 M6 [1 [# j4 V* l
  15.     defconsensus():
    % C/ I/ g7 |" C% O1 }( ~" w
  16.     replaced=blockchain.resolve_conflicts()- k. U+ l& [# ^0 u8 V4 S9 Y
  17.     ifreplaced:
    9 Q$ O4 P' U) B# `
  18.     response={
    ' _( C# P  z1 |6 Y' w
  19.     'message':'Ourchainwasreplaced',
    : f( u5 l) B6 O2 L; u& C# p/ x/ e+ J
  20.     'new_chain':blockchain.chain
    0 N  O7 I" G9 U" b2 a$ c
  21.     }3 ^" Q' ^  C! {5 u- z) o6 i9 H! P
  22.     else:" T* R, u- w; n# f
  23.     response={
    ; L: H5 ]- Q0 B2 l6 f- w3 q
  24.     'message':'Ourchainisauthoritative',' C, k- ~# [/ y: V
  25.     'chain':blockchain.chain
    ) G3 c: R( S/ l
  26.     }8 c* W# S* M) x! w
  27.     returnjsonify(response),200
    , F5 G( p9 m6 s/ O3 D) l7 t- E
  28.     ```
复制代码

8 `. y( G( J0 S4 \6 }- a' g; r1 m    你可以在不同的机器运行节点,或在一台机机开启不同的网络端口来模拟多节点的网络,这里在同一台机器开启不同的端口演示,在不同的终端运行一下命令,就启动了两个节点:```http://localhost:5000```和```http://localhost:5001```。
8 T& Z* w+ p3 ^
& ]0 W0 j' T7 ?) U    -注册一个新的节点-
6 x0 A1 A+ [! T4 p    然后在节点2上挖两个块,确保是更长的链,然后在节点1上访问GET/nodes/resolve,这时节点1的链会通过共识算法被节点2的链取代。
标签: Python
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

江左没浪 小学生
  • 粉丝

    18

  • 关注

    0

  • 主题

    7