Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

用 Python 从零开始创建区块链

江左没浪
321 3 0
我们都对比特币的崛起感到惊讶惊奇,并且想知道其背后的技术——区块链是如何实现的。# ?- b3 A% j. p- e$ Q: \
    但是完全搞懂区块链并非易事,至少对我来讲是这样。我喜欢在实践中学习,通过写代码来学习技术会掌握得更牢固。通过构建一个区块链可以加深对区块链的理解。8 |: V$ M4 C* u1 w3 ~
    准备工作& U  a( L; P+ b: `7 V5 r% H8 z
    我们知道区块链是由区块的记录构成的不可变、有序的链结构,记录可以是交易、文件或任何你想要的数据,重要的是它们是通过哈希值(hashes)链接起来的。$ b  p: j- p8 B" B( u) ^" y
    如果你不知道哈希值是什么,这里有一个解释。) P3 c7 U: R& x3 v% ^# k
    这份指南的目标人群:阅读这篇文章,要求读者对Python有基本的了解,能编写基本的Python代码,并且需要对HTTP请求有基本的理解。  t8 h5 G( \/ G# [" }
    环境准备:确保已经安装Python3.6+,pip,Flask,requests。安装方法:
* |- u" ?5 F; H, q% D1 J    pipinstallFlask==0.12.2requests==2.18.4
6 F' l+ A' \8 T+ y3 j    同时还需要一个HTTP客户端,比如Postman,cURL或其它客户端。
2 w0 O( Y! C$ f0 g0 i5 U" q( `1 l    一、开始创建BlockChain
* I- h: c& q0 r3 r: g+ n    打开你最喜欢的文本编辑器或者IDE,比如PyCharm,新建一个文件blockchain.py,本文所有的代码都写在这一个文件中。如果你丢了它,你总是可以引用源代码。% O' Q* a2 C6 ?, S
    BlockChain类7 m* K8 G1 |3 r9 h
    首先创建一个Blockchain类,在构造函数中创建了两个列表,一个用于储存区块链,一个用于储存交易。
$ |) M& P" z* w, N* ?0 d    以下是BlockChain类的框架:
; S; Z6 s# i6 O5 k, l  v8 D6 {  
  1. classBlockchain(object):
    + B3 T8 F9 \; D. E; V% p
  2.     def__init__(self):/ K  b$ {5 W9 j8 I* o
  3.     self.chain=[]
    / {& ?1 k0 `) z6 }6 S1 I- p5 H( S
  4.     self.current_transactions=[]8 D, z4 U  |3 p0 j( A& I
  5.     defnew_block(self):
    / x& K" q- e) h9 R) k  R
  6.     #CreatesanewBlockandaddsittothechain
    9 u: R3 l6 u. g
  7.     pass
    8 Y3 o/ w3 S4 M: g
  8.     defnew_transaction(self):
    0 ?% z" F7 d  u8 ]
  9.     #Addsanewtransactiontothelistoftransactions1 o" @# c, q, t
  10.     pass
    & |' E" C( u8 [
  11.     @staticmethod5 D& `2 R; e. C$ x9 y* ^& I
  12.     defhash(block):, ?+ X0 \/ k% u4 ^/ }) d
  13.     #HashesaBlock; }3 I" M6 j2 L7 F% e* B. L; L
  14.     pass
    # Z/ z& I6 ^: R9 |3 P) d6 Y- ~
  15.     @property+ F8 ]+ m/ R$ x8 W5 b& L2 Z/ ?+ z2 O
  16.     deflast_block(self):
    3 ?' c; x& E2 I# d, T$ J/ m
  17.     #ReturnsthelastBlockinthechain# j' F9 D  p6 z4 b
  18.     pass
复制代码
3 ?/ B0 O$ x- Z$ O+ B4 ~
    -我们的区块链类的蓝图-3 _3 r" U% E2 b
    Blockchain类用来管理链条,它能存储交易,加入新块等,下面我们来进一步完善这些方法。! V: g( ?. ?" Q. D
    块结构
; r+ A9 y7 N$ c4 g1 c( y% o/ \    每个区块包含属性:索引(index),Unix时间戳(timestamp),交易列表(transactions),工作量证明(稍后解释)以及前一个区块的Hash值。
2 C0 y9 S! ^" ^8 I5 j* b    以下是一个区块结构:1 H; H  R; F* k( F: x5 M  t/ Q% N
   
  1. block={
    . f, f4 T5 T, ^; Q6 u
  2.     'index':1,
    % o1 h& _9 d4 G! y$ Z* n1 T, {
  3.     'timestamp':1506057125.900785,3 i0 ^0 x+ K1 A( K6 _
  4.     'transactions':[" q, x2 D% v- h
  5.     {
    + `9 o/ A* b( w' k
  6.     'sender':"8527147fe1f5426f9dd545de4b27ee00",
    3 L  l: |3 ?2 S* \: M6 w
  7.     'recipient':"a77f5cdfa2934df3954a5c7c7da5df1f",, G8 \, Z  O% |. G+ G; d
  8.     'amount':5,
    : H8 l' r4 l  e" m5 w  B9 r
  9.     }8 ~; S- \: v8 l' E
  10.     ],& q5 ?! {+ S5 A& l
  11.     'proof':324984774000,
    : P0 N1 a  K, C( D3 s
  12.     'previous_hash':"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
    5 q4 X2 z! D: \6 g% T; I
  13.     }
复制代码
9 o1 B2 m( Y/ U% N* @1 P  {) w
    -链上一个区块的例子-
! A0 G, p& i  r; _9 e% N2 p    到这里,区块链的概念就清楚了,每个新的区块都包含上一个区块的Hash,这是关键的一点,它保障了区块链不可变性。如果攻击者破坏了前面的某个区块,那么后面所有区块的Hash都会变得不正确。
  o- g0 M7 O; x: b9 D: y4 ^    确定这有用吗?嗯,你可以花点彻底了解它——这可是区块链背后的核心理念。' P  y5 p; @% Y0 f$ ?7 {
    加入交易. Y. ~! I8 R  w& a& r7 W' w2 g9 Q
    接下来我们需要添加一个交易,来完善下new_transaction()方法。3 R5 ^* A; _+ ]
  
  1. classBlockchain(object):
    + ^; n% w. S3 D
  2.     ...$ E. d* j& {9 A6 K
  3.     defnew_transaction(self,sender,recipient,amount):3 R# q; L5 |3 s) v* O  H8 s# ]
  4.     """
    5 z' W* S5 U, ^8 h! A
  5.     生成新交易信息,信息将加入到下一个待挖的区块中
    0 ?/ D/ {6 E% Y4 j6 ~
  6.     :paramsender:AddressoftheSender
    ! [3 a" R, \7 O6 u
  7.     :paramrecipient:AddressoftheRecipient
    % H' k$ l" i' p
  8.     :paramamount:Amount- M6 u/ `: @* ]3 h  `, f3 I: }
  9.     :return:TheindexoftheBlockthatwillholdthistransaction
    6 r9 h: X9 s' t8 R5 x1 i
  10.     """. @  `: @, B! o, Y/ c' g
  11.     self.current_transactions.append({
    7 s. w5 `) E) `( i" G! o
  12.     'sender':sender,4 u+ }2 {4 X$ \# n
  13.     'recipient':recipient,
    7 u$ v  [, R6 u) {0 h. O
  14.     'amount':amount,
    ' g6 M" y+ P! D" }  Z
  15.     })
    / }! v* y7 g6 e* F/ V4 G
  16.     returnself.last_block['index']+1
复制代码
2 n7 l6 f' G0 n( m5 z8 F- E+ r$ y
    new_transaction()方法向列表中添加一个交易记录,并返回该记录将被添加到的区块(下一个待挖掘的区块)的索引,等下在用户提交交易时会有用。
: u' y$ b+ g9 L! _4 J) W8 v) r' q    创建区块5 R8 f4 }+ A1 T5 ~2 Q4 E/ C
    当Blockchain实例化后,我们需要构造一个创世块(没有前区块的第一个区块),并且给它加上一个“工作量证明”。+ v4 i3 b$ V+ }8 A4 M- k
    每个区块都需要经过工作量证明,俗称挖矿,稍后会继续讲解。1 j9 i7 Z$ s: e% _3 ~, F, V
    为了构造创世块,我们还需要完善new_block()、new_transaction()和hash()方法:; Q* o: a3 e* A, p& D
  
  1. importhashlib
    ; ~/ V' u1 X2 n0 B  d
  2.     importjson* q1 F: Y* t. L3 f: J$ n: y. z
  3.     fromtimeimporttime
    ' b' q% Q9 Q; J3 g' h; N0 i2 p6 x
  4.     classBlockchain(object):& @% B9 g2 ]9 u* t
  5.     def__init__(self):
    : P# G+ _" l' T! x- t0 l: t1 V) B
  6.     self.current_transactions=[]5 e' `% a" J# A
  7.     self.chain=[]
    1 X- j5 A6 ?- a: O/ q7 Z  v3 K
  8.     #Createthegenesisblock
    + b7 @1 f& i7 a3 |
  9.     self.new_block(previous_hash=1,proof=100)
    % l9 Z' z5 B. t! g
  10.     defnew_block(self,proof,previous_hash=None):, _5 r2 `7 K8 K% F3 o
  11.     """
    3 s4 R! W! Z6 m7 R; `
  12.     生成新块0 w% i1 }0 |- P
  13.     :paramproof:TheproofgivenbytheProofofWorkalgorithm/ X% i2 M  s( j+ K  q) y
  14.     :paramprevious_hash:(Optional)HashofpreviousBlock. `- ]* a8 w/ Z0 N8 C0 X
  15.     :return:NewBlock
    % k% d2 v4 }/ x, q6 v/ m
  16.     """: v  x: b, f$ h' k) ~- w
  17.     block={
    ' |4 c( B4 f4 F6 Q' A0 e
  18.     'index':len(self.chain)+1,
    7 ]7 P+ e- o; G/ _8 a1 Y
  19.     'timestamp':time(),2 u- b9 A4 V( o$ l9 X0 H4 M+ }
  20.     'transactions':self.current_transactions,3 o. e1 o  T3 B& c
  21.     'proof':proof,
    2 X7 p: \# a/ A' J- \9 D% C
  22.     'previous_hash':previous_hashorself.hash(self.chain[-1]),
    0 ^$ f. i" w; ~6 |2 h
  23.     }# _  [/ Y1 y; e3 J
  24.     #Resetthecurrentlistoftransactions+ t7 m% I$ N% I: b$ i! N# ^
  25.     self.current_transactions=[]
    2 z; c: s8 u6 h; d- ~- j, S
  26.     self.chain.append(block)
    % s" W% A& o0 K/ T4 {
  27.     returnblock
    * T5 o$ W' S9 c1 r( |+ V/ g* H
  28.     defnew_transaction(self,sender,recipient,amount):
    6 ]. w/ q2 E9 t7 C. {( R1 Q! B
  29.     """
    3 X- B- n- w( Y3 E6 o
  30.     生成新的交易信息,信息将加入到下一个待挖的区块中4 Z0 K5 n: X) Y/ J8 `0 N& n3 b
  31.     :paramsender:AddressoftheSender
    , Z& {5 K8 t% z- q* \, i5 l
  32.     :paramrecipient:AddressoftheRecipient
    , M. f  b" M% k# ^
  33.     :paramamount:Amount" l9 _/ z" G) F
  34.     :return:TheindexoftheBlockthatwillholdthistransaction, l8 H5 s, ~7 A+ i% \  m1 ]
  35.     """
    ! q% A# g; l8 S( @, y
  36.     self.current_transactions.append({" B5 `3 I) [0 z  C1 b
  37.     'sender':sender,
    8 r& |, g4 Z' n9 p) W
  38.     'recipient':recipient,
    $ k% N( J  V# k$ n  f. V
  39.     'amount':amount,9 w/ `  M- x, H8 g! `
  40.     })
    4 P1 y# `4 d( Q1 V. w. y5 Y
  41.     returnself.last_block['index']+1; ~) o* V9 E, E# Y
  42.     @property
    * j4 l. M' t$ n" m, T1 N4 X9 W
  43.     deflast_block(self):5 o: z& q) f% e- @
  44.     returnself.chain[-1]8 V' F, O% r5 M1 o- k
  45.     @staticmethod8 x0 ?: o: K6 O2 G# N
  46.     defhash(block):! s- s( X, J4 b4 g9 V% u
  47.     """# |5 F  J/ n' Z$ B+ b
  48.     生成块的SHA-256hash值
    # I) J& D* o4 p9 q& W
  49.     :paramblock:Block7 R, \  ^2 I5 Q. O8 V; j$ B$ a( n1 q
  50.     :return:; c, D/ D* m9 n8 I: M( o" T$ y+ c
  51.     """
复制代码
/ z8 x( U  I7 `+ M
    #WemustmakesurethattheDictionaryisOrdered,orwe'llhaveinconsistenthashes' D9 p# K4 v0 v& w
    block_string=json.dumps(block,sort_keys=True).encode()) K0 G5 C1 O6 j6 [5 n( j
    returnhashlib.sha256(block_string).hexdigest()
8 v# c0 V" X6 N# [% T) u$ f# M7 e    上述应该是非常直接的——我已经添加了一些注释和字符来帮助大家理解。当表达出我们的区块链时,我们也快要完成了。但现在,你肯定在疑惑新区块是如何被创建、伪造或是挖出的。
) h0 R! r  A+ n. e: \  ?% c+ }# k    理解工作量证明
( i- `' L; n' E( S* x3 Q% |    新的区块依赖工作量证明算法(PoW)来构造。PoW的目标是找出一个符合特定条件的数字,这个数字很难计算出来,但容易验证。这就是工作量证明的核心思想。3 D+ Y) `# b# N7 x0 m6 `9 |: R
    为了方便理解,我们举个例子:1 [- z! V  n0 d
    假设一个整数x乘以另一个整数y的积的Hash值必须以0结尾,即hash(x*y)=ac23dc…0。设变量x=5,求y的值?用Python实现如下:
4 Y% Z) H, ]' S! f9 \8 ?" s
  1.   fromhashlibimportsha256
    ) n- l* t- ^7 k* M, j* h
  2.     x=5
    + g6 T, C+ s0 S+ g+ O
  3.     y=0#y未知: I" D6 o6 f0 B4 H3 l. K
  4.     whilesha256(f'{x*y}'.encode()).hexdigest()[-1]!="0":
    - l( J, ^5 p$ w( d: U" I
  5.     y+=1
    5 E' n/ b8 u. X( C4 ~# @4 Q4 Q
  6.     print(f'Thesolutionisy={y}')
复制代码
( |; K) X& e9 T  q& r; d. B& o. F
    结果y=21,因为:
' q+ ?% x7 p3 P# F/ @    hash(5*21)=1253e9373e...5e3600155e8604 H2 Y$ }1 x8 {" ]5 W5 ^
    在比特币中,使用称为Hashcash的工作量证明算法,它和上面的问题很类似。矿工们为了争夺创建区块的权利而争相计算结果。通常,计算难度与目标字符串需要满足的特定字符的数量成正比,矿工算出结果后,会获得比特币奖励。2 \6 a1 e# E  Q! l) S- Y2 W5 O9 t
    当然,在网络上非常容易验证这个结果。
/ O2 r, W8 L+ U, N6 d- \    实现工作量证明
0 K  `! ]. v0 w5 G/ ]) V& O    让我们来实现一个相似PoW算法,规则是:2 H0 p2 g5 L/ \9 ~7 M' R
    寻找一个数p,使得它与前一个区块的proof拼接成的字符串的Hash值以4个零开头。
8 E  f/ R' B# B+ _& S  
  1. importhashlib9 x" S) ^0 x8 R7 X% P
  2.     importjson
    , z7 E' A( s# o, w
  3.     fromtimeimporttime
    ' s8 a/ ^$ Z8 t7 K
  4.     fromuuidimportuuid4
    ) l9 d" U- l& u
  5.     classBlockchain(object):. h- d" p1 |, ]0 n1 F8 O' q* |4 V
  6.     ...
    ) {* l* O$ V; N" _& }6 k
  7.     defproof_of_work(self,last_proof):
    9 A# {! i  U6 Z: V( o
  8.     """% d' d1 Y" g6 M# x7 i- s
  9.     简单的工作量证明:
    # P' E5 H. ~% z" Y' ]7 i' t4 s
  10.     -查找一个p'使得hash(pp')以4个0开头
    9 Z3 W( D# o+ \; Y/ e6 O- q
  11.     -p是上一个块的证明,p'是当前的证明, n& ~% I) g: V
  12.     :paramlast_proof:8 w' J" x) X/ j
  13.     :return:6 b. e+ |( L- v! ]& D
  14.     """
      s+ A- a1 t) D  k& q6 R# i
  15.     proof=0% W  g. ^& p7 K  L' c0 F
  16.     whileself.valid_proof(last_proof,proof)isFalse:
    0 ~& j/ Z1 W" ~. ?" m+ e/ n
  17.     proof+=1  e# |. b: B" U! d; b; i* l- M
  18.     returnproof6 I/ Q3 B2 {8 c6 C2 L) A. m
  19.     @staticmethod
    8 Q+ a  a/ @  D# R4 f# e
  20.     defvalid_proof(last_proof,proof):
    2 Z6 a/ }# B# K% p( Y! _
  21.     """
    # V$ J2 g$ [5 `+ {& G; j1 T
  22.     验证证明:是否hash(last_proof,proof)以4个0开头?
    ( Q- c& o1 ?9 @* ^
  23.     :paramlast_proof:PreviousProof
    2 R) N; e5 O7 r1 R3 t) h
  24.     :paramproof:CurrentProof
    2 e7 {7 p6 n- i
  25.     :return:Trueifcorrect,Falseifnot.; V5 f. N9 m- }4 U7 r$ f+ g
  26.     """( h: Y! R2 {, q  M: J8 m
  27.     guess=f'{last_proof}{proof}'.encode(); s" Z' G3 C* X' X+ I
  28.     guess_hash=hashlib.sha256(guess).hexdigest()6 T0 H8 [& J4 B* [, L6 g% m
  29.     returnguess_hash[:4]=="0000"& h# y8 U% P- F  y+ w: V, e1 I
  30.     ```
复制代码
+ v( u3 o! h) {* h2 Z
    衡量算法复杂度的办法是修改零开头的个数。使用4个来用于演示,你会发现多一个零都会大大增加计算出结果所需的时间。6 y& v/ \0 \* L# C9 g6 s
    现在Blockchain类基本已经完成了,接下来使用HTTPrequests来进行交互。
" S9 g5 \5 [4 V( j2 A# b    ##二、BlockChain作为API接口
! P- B( J" P* x, U+ q* E    我们将使用PythonFlask框架,这是一个轻量Web应用框架,它方便将网络请求映射到Python函数,现在我们来让Blockchain运行在基于Flaskweb上。
9 ^4 Y7 ~# w6 ~' ?9 R    我们将创建三个接口:+ c& i3 F* m3 q2 D( z" s
   
  1. ```/transactions/new```创建一个交易并添加到区块
    # C$ f8 D. ~9 _2 P) H0 {, A
  2.     ```/mine```告诉服务器去挖掘新的区块" b2 p( H) n5 g4 k4 z; H' @! D7 |
  3.     ```/chain```返回整个区块链
    $ t& o: J( I1 p& ^
  4.     ###创建节点
    : Z% U2 O6 O* N; u  K, W& A3 g
  5.     我们的Flask服务器将扮演区块链网络中的一个节点。我们先添加一些框架代码:9 Q7 @$ [0 `$ l7 K0 d- I& b
  6.     importhashlib
    ) V; {* h% U$ Q, q9 z3 B/ F
  7.     importjson$ e9 r4 |. `  S2 G+ o1 ~
  8.     fromtextwrapimportdedent( |& F& K2 M' I$ r; ]/ q
  9.     fromtimeimporttime
    8 s5 y3 l9 S1 M3 _
  10.     fromuuidimportuuid4  ^0 L7 P6 `0 ^% ^! a) [( K& H# p
  11.     fromflaskimportFlask
    ' h. n" ~& U3 Y; T
  12.     classBlockchain(object):
    3 j. S& p  u$ R$ E/ M5 h: m! }
  13.     …3 I+ }6 F- e2 h6 d
  14.     InstantiateourNode
    0 K+ ^' X  M  c6 g$ W3 S) l
  15.     app=Flask(name)2 T8 e3 a7 Y7 B1 x
  16.     Generateagloballyuniqueaddressforthisnode8 l+ O6 H+ l  u* T! j- q$ J* c( U
  17.     node_identifier=str(uuid4()).replace(’-’,‘’)
    ' f, N5 v' X: M
  18.     InstantiatetheBlockchain
    1 P& P5 J3 N! D# p" l
  19.     blockchain=Blockchain()' j$ j+ [! ?; m0 V! _: `) R: g+ ], |
  20.     @app.route(’/mine’,methods=[‘GET’])
    / u+ a6 o  I  |7 f4 ~! J/ l
  21.     defmine():& ~6 {! K8 |3 a* a# b
  22.     return“We’llmineanewBlock”
    ( H  g! A0 v" e2 e
  23.     @app.route(’/transactions/new’,methods=[‘POST’])5 {; Z" P7 G: f0 Q% F5 _$ u
  24.     defnew_transaction():) s& i& P- i( n# f8 t- k0 y% n' F
  25.     return“We’lladdanewtransaction”
    . C" e' O7 u/ c# j
  26.     @app.route(’/chain’,methods=[‘GET’])
    - D  e5 y5 u6 W3 i% Z6 X8 v7 \
  27.     deffull_chain():" S; e* I6 [! X2 D& A
  28.     response={
    * ~  @+ H+ U* Q" @2 W
  29.     ‘chain’:blockchain.chain,
    3 r0 l8 D) U, ]' ]
  30.     ‘length’:len(blockchain.chain),
    * q- b- u8 ]1 y$ ]
  31.     }1 f; N- c: x, }' J+ u5 I
  32.     returnjsonify(response),200
    2 y0 Y. V  J+ a" ^# F
  33.     ifname==‘main’:4 M2 r3 [& z; x+ t. m
  34.     app.run(host=‘0.0.0.0’,port=5000)6 E# m' z: k: X: T( L3 [" X5 G" ]
  35.     ```8 \1 r5 \7 h) i1 [) p
  36.     简单的说明一下以上代码:
    % X* {9 {2 F1 ^. U! }
  37.     第15行:创建一个节点。在这里阅读更多关于Flask的东西。, {( ]$ f: D: Y9 n7 u7 k+ E! C# i
  38.     第18行:为节点创建一个随机的名字。! c' x9 P& m$ y6 d2 u' I
  39.     第21行:实例Blockchain类。/ V2 N& S" E1 m& u+ y
  40.     第24–26行:创建/mineGET接口。
    . N" |9 U. M' }- C% {
  41.     第28–30行:创建/transactions/newPOST接口,可以给接口发送交易数据。+ ?. [  J2 N; M' @, v6 z# [! X
  42.     第32–38行:创建/chain接口,返回整个区块链。
    & @2 Y6 X3 m( `
  43.     第40–41行:服务运行在端口5000上。+ A3 A( O! y, B3 L
  44.     发送交易6 Q( z3 _# {2 V- c
  45.     发送到节点的交易数据结构如下:/ u+ ^- O) C" d* ~1 |) {" q( F
  46.     {
    . L) F$ U9 g7 d. U. J+ W; Y
  47.     "sender":"myaddress",
    # G* C: L1 k6 `( o* g1 L, i+ U
  48.     "recipient":"someoneelse'saddress",' w8 U$ z; z, I! G: l) g
  49.     "amount":5
    . Z- o! u) u6 a, d! r" f: g* I4 E
  50.     }
    ' x* g# C3 A0 N
  51.     之前已经有添加交易的方法,基于接口来添加交易就很简单了:( r3 F0 t, J1 x9 w5 x
  52.     importhashlib
    " s2 J8 Q# o6 d( f0 X
  53.     importjson
    " l# c3 B* g' f  i- I# M1 B$ f# s
  54.     fromtextwrapimportdedent/ V& Q8 c  U# \4 U
  55.     fromtimeimporttime/ x# [# T8 @$ u* [
  56.     fromuuidimportuuid4, ]& v# o" ?/ @8 e* e
  57.     fromflaskimportFlask,jsonify,request4 }6 t6 a$ U# J5 r; J/ ?" c
  58.     ...
    ; [. D* l7 i/ e' f( S
  59.     @app.route('/transactions/new',methods=['POST'])* x# P6 u* j$ m2 h; e: ^0 m
  60.     defnew_transaction():, j: B4 F0 u  {/ t* u2 R% ]) T' [
  61.     values=request.get_json()/ _. K0 r" M) U4 O6 H3 z
  62.     #CheckthattherequiredfieldsareinthePOST'eddata
    $ y" v& Z: x% w2 o" E  E8 m
  63.     required=['sender','recipient','amount']) \8 L3 S. U: W
  64.     ifnotall(kinvaluesforkinrequired):6 l& z, x. {1 [7 V4 U$ ~3 o
  65.     return'Missingvalues',400) n1 j; v" F! G
  66.     #CreateanewTransaction. s0 S0 n$ t7 j
  67.     index=blockchain.new_transaction(values['sender'],values['recipient'],values['amount'])8 p5 ~& i, L. a) Q' _3 P
  68.     response={'message':f'TransactionwillbeaddedtoBlock{index}'}
    ; R+ W9 c: |& _$ v5 R) d+ a) x
  69.     returnjsonify(response),201
    " @) @$ k0 K+ X; z6 [2 N
  70.     ```
    ' c$ A  D+ @/ ~: s' C( c  K
  71.     -创建交易的方法-
    , m; Q: K& o7 p. q/ q# h
  72.     ###挖矿
    + I7 E4 I; {4 ~5 z: @. _
  73.     挖矿正是神奇所在,它很简单,做了一下三件事:; \6 K# V% ^4 u0 ?9 }- K
  74.     计算工作量证明PoW
    ! ^, I7 W; V" D9 N( [
  75.     通过新增一个交易授予矿工(自己)一个币
    4 r  i; ^6 o" c0 i( X
  76.     构造新区块并将其添加到链中
    3 A& r- m' o6 W2 o- H
  77.     importhashlib4 D' d- [& T, w" F% b; C. F. |
  78.     importjson. @! u6 |6 d( Q2 {! w
  79.     fromtimeimporttime
    ) ?' F( U! u; J+ d( O& b. ~1 m
  80.     fromuuidimportuuid4  O! m, R# |* |! l1 G! d( D. S
  81.     fromflaskimportFlask,jsonify,request
    ( O# I$ ~/ W4 Q0 h/ P: N7 K
  82.     …( w# h  ]6 p# x: Z# x* u2 y
  83.     @app.route(’/mine’,methods=[‘GET’])
    ) M; e! P0 i; u0 H" ?# H. {! ~/ \
  84.     defmine():
    & l8 c$ U$ E, [# ^
  85.     #Weruntheproofofworkalgorithmtogetthenextproof…
    ' T: y: q# k: _, w
  86.     last_block=blockchain.last_block; Y4 t9 x! k/ _) h' c# v% @- K
  87.     last_proof=last_block[‘proof’]
    ' T! d( N$ u0 {
  88.     proof=blockchain.proof_of_work(last_proof)
    / `) F( S6 F+ P# M9 l& q
  89.     #给工作量证明的节点提供奖励.
    6 V: f$ P% u6 E! |+ g  U/ C! i+ i
  90.     #发送者为"0"表明是新挖出的币.
    / a$ }! k( G& n: X& H
  91.     blockchain.new_transaction(
    ! j2 o5 {- b$ b: Q5 z# I& [! m
  92.     sender="0",1 }" y8 v) c5 R9 V2 `
  93.     recipient=node_identifier,
    $ A9 k' h) S# X0 n7 X
  94.     amount=1,/ K$ n) f( }$ P2 t: \9 A) C+ W! L
  95.     )0 w) F5 J, q, y5 x
  96.     #ForgethenewBlockbyaddingittothechain
    7 Q; v- x/ A' b% I9 C
  97.     previous_hash=blockchain.hash(last_block)
    7 S! |+ `2 f3 l0 b9 b4 t3 v7 b
  98.     block=blockchain.new_block(proof,previous_hash)) g0 q- M. {& j5 A
  99.     response={
      u" [0 E7 m) i0 l* L) `& W
  100.     'message':"NewBlockForged",
    5 W$ W6 K0 Z) A  {* K
  101.     'index':block['index'],
    0 H% m+ e/ X' z/ T$ W% `  V
  102.     'transactions':block['transactions'],
    + P. U4 }  V9 E: X1 d' a' O, ?: ?: p
  103.     'proof':block['proof'],  b# ^, b. \* p/ K3 e4 p+ R6 t
  104.     'previous_hash':block['previous_hash'],
    " [2 r$ N, R2 a5 \8 B
  105.     }! d5 ]% T" j7 _
  106.     returnjsonify(response),2005 R+ m# A% K  X8 P
  107.     ```
复制代码

, X( P; ?0 n+ b7 v) @    注意交易的接收者是我们自己的服务器节点,我们做的大部分工作都只是围绕Blockchain类方法进行交互。到此,我们的区块链就算完成了,我们来实际运行下。
3 B. j6 @1 L0 ^3 O0 A" i8 x. ]    三、运行区块链8 F$ K. M$ @+ k# ?7 o2 ]
    你可以使用cURL或Postman去和API进行交互2 a7 z# p5 x! O: K
    启动server:9 H0 C& i! H( w/ _& h* t( o
    $pythonblockchain.py+ Y& O8 T2 Q- j+ f
    *Runningonhttp://127.0.0.1:5000/(PressCTRL+Ctoquit)
4 b& d& T4 w2 M0 s3 s    让我们通过发送GET请求到http://localhost:5000/mine来进行挖矿:
0 x8 R1 c( f7 K3 m    -使用Postman以创建一个GET请求-
. P; X$ e3 Y/ q8 V$ h' @* Y- n1 i    通过发送POST请求到http://localhost:5000/transactions/new,添加一个新交易:
' ~2 K1 V6 |& v5 n9 G% w* ?. W% s    -使用Postman以创建一个POST请求-
  V2 F- ]: W6 d* ~( B    如果不是使用Postman,则用一下的cURL语句也是一样的:/ ~# j2 P2 f$ W* ?
    $curl-XPOST-H"Content-Type:application/json"-d'{
" i( E7 m5 q. L2 F9 k6 {) Q    "sender":"d4ee26eee15148ee92c6cd394edd974e",: c$ N! \8 f* S& M# `0 z
    "recipient":"someone-other-address",
' y* c' `) n  q& o" k) O    "amount":55 S4 D/ ~( n  m+ [
    }'"http://localhost:5000/transactions/new"
- F6 T0 P, R" Y! ~' y4 n) z2 S    在挖了两次矿之后,就有3个块了,通过请求http://localhost:5000/chain可以得到所有的块信息:
8 M  v! b1 F8 w7 e5 Y+ v   
  1. {7 y8 {+ B* _5 m9 O
  2.     "chain":[
    ( u) p% N' L3 j# z" H( o& X
  3.     {5 X! e7 b6 Z5 ]. c( l0 w5 B. g2 C/ M
  4.     "index":1,
    ; u: `) F/ Q# R- D7 X( Q
  5.     "previous_hash":1,
    ! A. r# F( f- Z2 H4 X+ ?' O' {) b
  6.     "proof":100,
    " o+ x0 U1 X2 ?. o  Q& g
  7.     "timestamp":1506280650.770839,3 o* Y$ ^: k. R! k
  8.     "transactions":[]/ ^8 `* Y$ O8 D1 Y& k
  9.     },; ^& Q2 D5 m  q, S1 w, _
  10.     {
    % P: g& k7 p" l" A
  11.     "index":2,' l) P4 X9 j5 y% F3 k7 n* Z( l
  12.     "previous_hash":"c099bc...bfb7",
    " N/ @( N* W+ C4 h
  13.     "proof":35293,
    " a3 R# r3 m: N# {4 g
  14.     "timestamp":1506280664.717925,
    $ t: c- Q7 u2 L7 W4 }9 P0 i
  15.     "transactions":[, X; r. v! P/ l# I/ F+ G
  16.     {
    7 }- }9 E5 H& D; ^7 n, ]
  17.     "amount":1,
    / L8 Q( c8 k8 A' E9 V1 }# c
  18.     "recipient":"8bbcb347e0634905b0cac7955bae152b",
    9 _$ t" Z- e% |& @; X2 h
  19.     "sender":"0"
    ( o# g  W6 D& h, p
  20.     }
    , ]$ N" U9 K# ^5 m" D6 ]
  21.     ]
    ' ?  ^' ]: v1 f' i$ v# i
  22.     },. ]/ N! n/ L- ]/ D1 B( a
  23.     {& s+ F& o; h0 S) o- f9 u. O6 J
  24.     "index":3,
    : }6 ~. U6 `6 c2 g( i& X
  25.     "previous_hash":"eff91a...10f2",4 I5 p  |/ {. Q% `9 Q' \- t1 t" r
  26.     "proof":35089,2 [; ]! s2 E$ v
  27.     "timestamp":1506280666.1086972,
    8 [- l: ~5 O) e. N7 h1 M
  28.     "transactions":[( m8 ?' j. R) h; k- E3 T
  29.     {9 u: D* m- d; U5 n
  30.     "amount":1,( G& D* b! N# n! b
  31.     "recipient":"8bbcb347e0634905b0cac7955bae152b",& a* k' p3 }: U  s8 q1 g
  32.     "sender":"0"
    0 I# Y% V1 o8 n( z& [
  33.     }
    ! }( H# {& O5 Q, f# `3 q
  34.     ]
    2 R& h0 T2 X! ]' T0 A
  35.     }! _% F7 d$ F$ a9 D! j/ P* R! ]
  36.     ],3 Y' d+ E, E8 e7 F4 p
  37.     "length":35 \$ b4 I  m1 N% ?6 d' i
  38.     }
复制代码

: ?+ U! I% Y: u  J9 s  ]" U    四、一致性(共识); i( x: K" B/ W% {0 s
    非常棒,我们已经有了一个基本的区块链可以接受交易和挖矿。但是区块链系统应该是分布式的。既然是分布式的,那么我们究竟拿什么保证所有节点有同样的链呢?这就是一致性问题,我们要想在网络上有多个节点,就必须实现一个一致性的算法。
8 y) ~' |1 h' A1 ]    注册节点# g: V" a1 y  k$ u1 K+ e! {6 A
    在实现一致性算法之前,我们需要找到一种方式让一个节点知道它相邻的节点。每个节点都需要保存一份包含网络中其它节点的记录。因此让我们新增几个接口:
1 E( a% J. m2 R5 y) o1 O$ d! N1 t    /nodes/register接收URL形式的新节点列表; I' w7 X6 X) H) P: s$ o6 Q+ W
    /nodes/resolve执行一致性算法,解决任何冲突,确保节点拥有正确的链
, P4 f8 W8 x+ q    我们修改下Blockchain的init函数并提供一个注册节点方法:, a0 y3 H- Y3 R1 m& J
  
  1. ...
    ; _- P1 `% g7 A. ]; {; m
  2.     fromurllib.parseimporturlparse- E1 t/ |8 S6 a$ h+ t- f% E
  3.     ...& ^2 G% V. r8 i
  4.     classBlockchain(object):6 A! K* K6 _2 j0 k( |  A, ?9 v/ V
  5.     def__init__(self):
    ! ]1 h$ [7 v- s' B2 R; j3 Z
  6.     ...* U9 Y* B5 D% I
  7.     self.nodes=set()
    ( B' i& s# B0 ^$ [
  8.     ..., h6 r0 R) t6 h8 c7 e4 A' N; t
  9.     defregister_node(self,address):* N5 s6 O2 z# p0 o
  10.     """2 k" r# y- _2 K- l1 ^
  11.     Addanewnodetothelistofnodes
    3 L2 G% K* d: t2 a$ v
  12.     :paramaddress:Addressofnode.Eg.'http://192.168.0.5:5000'
    9 r  y4 [7 X* H; t' l7 ?& p
  13.     :return:None
    # z7 z4 V9 E4 U9 j
  14.     """
    4 K& E# @3 X' b# X/ h6 f
  15.     parsed_url=urlparse(address)
    * _8 V4 X9 i9 I8 M  v
  16.     self.nodes.add(parsed_url.netloc)
    ' I& ?' D8 Y0 x& n/ k; T. Z! X
  17.     ```
    ) u4 _, B" _1 `1 X# o$ A
  18.     我们用set()来储存节点,这是一种避免重复添加节点的简单方法——这意味着无论我们添加特定节点多少次,它实际上都只添加了一次。, k% d  W1 `$ c6 q' a0 s
  19.     ###实现共识算法
    6 p: ]1 o! m. V/ E
  20.     前面提到,冲突是指不同的节点拥有不同的链,为了解决这个问题,规定最长的、有效的链才是最终的链,换句话说,网络中有效最长链才是实际的链。我们使用以下的算法,来达到网络中的共识。" p4 I5 [# |3 h2 z% S# z9 ]
  21.     …
    ( V0 |. z' D6 n6 o& {# {
  22.     importrequests
    ' G% V+ ?! e0 K
  23.     classBlockchain(object)
    7 @2 T! u9 o. v3 o8 A+ [( g  X- A
  24.     …
    1 r8 w- X/ ~5 a, \! v; c
  25.     defvalid_chain(self,chain):
    1 {0 X1 q' H4 @" u; u/ `+ X
  26.     """
    : l5 u5 o+ G: o! E2 |, c
  27.     Determineifagivenblockchainisvalid; P% A, w4 q8 [+ J3 n9 B
  28.     :paramchain:Ablockchain
    + A9 ~& e, S9 Y0 O/ [& v" U
  29.     :return:Trueifvalid,Falseifnot
    8 R1 W6 l& W: g! U3 R" V; [
  30.     """5 ^0 `+ _8 o: W6 ^& R, o$ E% W
  31.     last_block=chain[0]
      Y! Y. t5 h$ J6 t1 P0 a
  32.     current_index=11 F3 E& w$ T1 v& A! w6 ^# s
  33.     whilecurrent_indexmax_lengthandself.valid_chain(chain):% ]3 n$ |0 o+ V2 t# k
  34.     max_length=length
    2 [7 |4 `5 O; A" V+ v
  35.     new_chain=chain- ]2 P- k; B5 c& ~
  36.     #Replaceourchainifwediscoveredanew,validchainlongerthanours
    + E1 E, ?& y: s( O" O
  37.     ifnew_chain:% k/ c2 B; c" _
  38.     self.chain=new_chain
    4 z$ l# V* x; [4 ]
  39.     returnTrue7 {& n8 b5 o& l- j" Z
  40.     returnFalse
    ( r% P4 M* o7 H! l2 l: {: r, {
  41.     ```
复制代码

1 S; x& `9 f% e0 e" Q( l$ V    第1个方法valid_chain()用来检查是否是有效链,遍历每个块验证hash和proof。+ ^4 G' F- b: ?
    第2个方法resolve_conflicts()用来解决冲突,遍历所有的邻居节点,并用上一个方法检查链的有效性,如果发现有效更长链,就替换掉自己的链。0 M) d( g* x) W+ _9 H8 T. x
    让我们添加两个路由,一个用来注册节点,一个用来解决冲突。! y$ a+ u2 h; Y( Z' i% @
   
  1. @app.route('/nodes/register',methods=['POST']). n( C/ i" @, _9 r+ n
  2.     defregister_nodes():. J$ N; x& C1 O% j* v* {# @% T6 ]1 w
  3.     values=request.get_json()) _1 `* |6 l/ r8 {5 r* V
  4.     nodes=values.get('nodes')6 Z( \) o1 n/ T2 B5 l, a
  5.     ifnodesisNone:9 m! E0 v. e" t0 d
  6.     return"Error:Pleasesupplyavalidlistofnodes",400' q/ E! E6 W0 g  R3 u
  7.     fornodeinnodes:# v$ G( R7 Q4 H
  8.     blockchain.register_node(node)% N' ?( z5 n8 s+ _
  9.     response={
    " J1 J+ ?; O6 ?
  10.     'message':'Newnodeshavebeenadded',3 J( R& ~" D$ p2 x- K
  11.     'total_nodes':list(blockchain.nodes),
    $ F% h+ j& V9 @6 R! z) V
  12.     }+ t3 l5 W, ?* ^6 P
  13.     returnjsonify(response),201
    & y4 u# f: n1 ~% j' ~' I, e8 x' {
  14.     @app.route('/nodes/resolve',methods=['GET'])
    ! h, @# E" N# N5 l6 F) @8 K- Q
  15.     defconsensus():
    # e' n6 f+ e$ Y3 l! a: w8 x% H1 d
  16.     replaced=blockchain.resolve_conflicts()& b% i5 h' s, _) I: f
  17.     ifreplaced:% M) s) Z7 i( F" X$ C& D
  18.     response={' ^+ @; r) K" y+ l
  19.     'message':'Ourchainwasreplaced',
    ! j( s! ^5 t7 j2 I5 B. z3 N! [8 Y
  20.     'new_chain':blockchain.chain+ a& o& Z2 `3 o" |: L2 ^( f
  21.     }' ^' `2 f) C& K: o* H( D: R/ A
  22.     else:
    / L3 i% `+ _# d% H7 e
  23.     response={) I6 Z) C9 o9 W
  24.     'message':'Ourchainisauthoritative',( O4 x( d7 {! K  K- O' Y
  25.     'chain':blockchain.chain
    / }5 l/ W3 P* o9 Q. T  K
  26.     }5 ~/ g  }: s9 N1 j. E0 |. D
  27.     returnjsonify(response),200
    7 m$ x; Y1 j2 a! M
  28.     ```
复制代码
/ a: R. x2 ^* H. s+ z0 u& C( h
    你可以在不同的机器运行节点,或在一台机机开启不同的网络端口来模拟多节点的网络,这里在同一台机器开启不同的端口演示,在不同的终端运行一下命令,就启动了两个节点:```http://localhost:5000```和```http://localhost:5001```。3 R9 c' {) @& H) n$ @
# Q5 t; ^9 k2 v! w" o! u  T
    -注册一个新的节点-
7 T+ {" a2 a4 G/ g4 {; K; J- a    然后在节点2上挖两个块,确保是更长的链,然后在节点1上访问GET/nodes/resolve,这时节点1的链会通过共识算法被节点2的链取代。
标签: Python
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

江左没浪 小学生
  • 粉丝

    18

  • 关注

    0

  • 主题

    7