Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

用 Python 从零开始创建区块链

江左没浪
309 3 0
我们都对比特币的崛起感到惊讶惊奇,并且想知道其背后的技术——区块链是如何实现的。% {2 q% g! V8 z. r( o
    但是完全搞懂区块链并非易事,至少对我来讲是这样。我喜欢在实践中学习,通过写代码来学习技术会掌握得更牢固。通过构建一个区块链可以加深对区块链的理解。7 O0 n. W  J; C+ `) ^0 \+ `0 t
    准备工作
* v: T8 O; x8 w0 Y8 H* P2 @    我们知道区块链是由区块的记录构成的不可变、有序的链结构,记录可以是交易、文件或任何你想要的数据,重要的是它们是通过哈希值(hashes)链接起来的。
0 E* \3 _, t. U, Z, P7 x6 a    如果你不知道哈希值是什么,这里有一个解释。
3 a: S& o, j4 }, O" w! Y    这份指南的目标人群:阅读这篇文章,要求读者对Python有基本的了解,能编写基本的Python代码,并且需要对HTTP请求有基本的理解。( G7 y0 e  a) a  i5 v: U3 L
    环境准备:确保已经安装Python3.6+,pip,Flask,requests。安装方法:7 y* d9 B! {) D0 y; b& y0 Y
    pipinstallFlask==0.12.2requests==2.18.4
+ m" z% W; i+ t' r9 y    同时还需要一个HTTP客户端,比如Postman,cURL或其它客户端。
/ C3 ~* ?/ x2 d% p. v8 k1 T    一、开始创建BlockChain  r  t- Q' E1 X- ^
    打开你最喜欢的文本编辑器或者IDE,比如PyCharm,新建一个文件blockchain.py,本文所有的代码都写在这一个文件中。如果你丢了它,你总是可以引用源代码。
, Z" z# M% w/ c  \/ `. W    BlockChain类
0 R1 @* \; c& c: X  @- @: E    首先创建一个Blockchain类,在构造函数中创建了两个列表,一个用于储存区块链,一个用于储存交易。
- q" P2 h" C; j* p; @: |5 D    以下是BlockChain类的框架:
+ q, X: f0 w+ c- b3 a% O  
  1. classBlockchain(object):
    3 ~& f* `" H9 ~/ p% h5 z# j2 f
  2.     def__init__(self):& l: d, q; _* j, x% {
  3.     self.chain=[]
    # E8 _3 s/ M# T  }! F
  4.     self.current_transactions=[]
    ' C/ [& [2 P& b$ W7 I/ p
  5.     defnew_block(self):* d8 W1 {9 t9 J( e" l8 D
  6.     #CreatesanewBlockandaddsittothechain" D! E, ^2 E; |  _5 ]4 @  y/ Q
  7.     pass- I! P( v4 S! b8 o
  8.     defnew_transaction(self):8 w9 e' Q% k' S9 w! n
  9.     #Addsanewtransactiontothelistoftransactions
    8 S1 |3 t3 B& g: t+ E; a# q
  10.     pass* l- H  ~- k) f9 @# E* m
  11.     @staticmethod
    , q$ X* v6 ]2 s# j1 C) y
  12.     defhash(block):8 @. u0 g. a" U- U! A2 v* G! H# F
  13.     #HashesaBlock
    ! }7 @- q- p/ G5 D0 l
  14.     pass+ C) b$ h1 r$ u5 P
  15.     @property* u# H, o; A/ x) l
  16.     deflast_block(self):2 d% f4 y* {) a. q& X$ w
  17.     #ReturnsthelastBlockinthechain& s6 k  X) p& l% @
  18.     pass
复制代码

' V. l7 ~% b9 R    -我们的区块链类的蓝图-
+ ^) `8 F- L* D- U; k+ s- P    Blockchain类用来管理链条,它能存储交易,加入新块等,下面我们来进一步完善这些方法。) Z8 h) v; D- r8 g* B% |' j
    块结构
" Z7 B: p0 `" \5 ~    每个区块包含属性:索引(index),Unix时间戳(timestamp),交易列表(transactions),工作量证明(稍后解释)以及前一个区块的Hash值。. k$ g! R" v7 w3 K( x; G6 U
    以下是一个区块结构:
, y8 m- m" w( l6 @6 R   
  1. block={- b: A* y; r- u. X" M' V
  2.     'index':1,0 p% Z6 \! M9 Y: ^" n
  3.     'timestamp':1506057125.900785,) ]( j. i) [9 o
  4.     'transactions':[
    % M$ r2 P  M4 S5 q+ R5 l
  5.     {
    + |) _( Y' ?: d
  6.     'sender':"8527147fe1f5426f9dd545de4b27ee00",9 d5 s2 {1 t8 P1 w6 \& C
  7.     'recipient':"a77f5cdfa2934df3954a5c7c7da5df1f",4 w6 g8 }2 H  Q4 {4 ^' l
  8.     'amount':5,. w: X- E+ t% `/ R. _5 c3 |1 l
  9.     }
    ) N: F! n2 H# n( ]9 x7 O
  10.     ],, C4 H: Q1 v5 O6 l* G1 `7 a" W7 |& C
  11.     'proof':324984774000,% L( {: j1 S$ o4 Y
  12.     'previous_hash':"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
    : e' r4 F# d( }
  13.     }
复制代码

5 C/ o8 B4 i8 H    -链上一个区块的例子-
3 b7 g) Q6 J3 i7 F    到这里,区块链的概念就清楚了,每个新的区块都包含上一个区块的Hash,这是关键的一点,它保障了区块链不可变性。如果攻击者破坏了前面的某个区块,那么后面所有区块的Hash都会变得不正确。. I' T8 |; h# H4 k* S
    确定这有用吗?嗯,你可以花点彻底了解它——这可是区块链背后的核心理念。
. k+ S  F( e5 c& Y9 m* T1 _    加入交易, l! ?. C, I1 h5 s) M- |7 Q
    接下来我们需要添加一个交易,来完善下new_transaction()方法。3 |3 h5 @" e# {8 E, `2 g5 e
  
  1. classBlockchain(object):
    6 ^% D: K0 D# X$ [$ M8 e" l
  2.     ...
    ; X8 o. p2 c: n+ r
  3.     defnew_transaction(self,sender,recipient,amount):1 t7 M: r; n; x: c: O9 n$ l' U
  4.     """" L, M8 R" O1 S2 ?; s
  5.     生成新交易信息,信息将加入到下一个待挖的区块中
    - |3 }  ^" N# E1 ^
  6.     :paramsender:AddressoftheSender
    7 d5 O% r2 R5 o1 _
  7.     :paramrecipient:AddressoftheRecipient
    3 z# G. @) B6 y  b* C% f  O
  8.     :paramamount:Amount% R9 M) }1 _* v
  9.     :return:TheindexoftheBlockthatwillholdthistransaction
    ) O6 J; v, e! J. B6 q; K
  10.     """9 ?# |! Q: G9 B
  11.     self.current_transactions.append({$ z4 w  e7 B; H
  12.     'sender':sender,
    : v( D! I8 K- W
  13.     'recipient':recipient,
    ( t8 E) _6 C  U  Y+ V
  14.     'amount':amount,
    + v! S* V! B/ Y* Y& G  c
  15.     })
    $ H; x# t' O4 [' }
  16.     returnself.last_block['index']+1
复制代码

" j" k5 t: F1 J. h+ S, J    new_transaction()方法向列表中添加一个交易记录,并返回该记录将被添加到的区块(下一个待挖掘的区块)的索引,等下在用户提交交易时会有用。
; K! z. Y9 s4 r    创建区块
% i  `+ b) s+ d( g1 h/ r! C/ u    当Blockchain实例化后,我们需要构造一个创世块(没有前区块的第一个区块),并且给它加上一个“工作量证明”。  [+ J8 V& k' {% B7 z" h% i7 p
    每个区块都需要经过工作量证明,俗称挖矿,稍后会继续讲解。& [+ g: A* o- m+ x% `4 O2 ^
    为了构造创世块,我们还需要完善new_block()、new_transaction()和hash()方法:  m* F& c* z1 @- |# C
  
  1. importhashlib; p- h+ y0 X' }
  2.     importjson& p7 o5 ^8 p) ^& r
  3.     fromtimeimporttime' P+ ?& X9 j3 j* c9 `
  4.     classBlockchain(object):
    . k9 ~5 l4 b5 d1 S7 N0 Y* e0 K
  5.     def__init__(self):
    , @/ w* V0 f7 w' w
  6.     self.current_transactions=[]
    ) y& a( C1 o$ n( Z3 X' ^- J2 l
  7.     self.chain=[]
    6 G, P8 ~: s6 @( B; p6 G
  8.     #Createthegenesisblock
    * B; G' h( M, c$ c/ n6 ^4 G" C
  9.     self.new_block(previous_hash=1,proof=100)! u( y  `/ w# |# F  p# I. f0 \
  10.     defnew_block(self,proof,previous_hash=None):
    " v2 h/ [& [2 a# [
  11.     """
    . @- J6 |! @) W2 p' p& y
  12.     生成新块2 V4 M% L1 U/ }4 Z+ z
  13.     :paramproof:TheproofgivenbytheProofofWorkalgorithm
      V8 s1 m! A) T4 m! ?
  14.     :paramprevious_hash:(Optional)HashofpreviousBlock
    . S7 S0 i1 M. X) Z
  15.     :return:NewBlock" O- B! Y5 l- {. E8 U3 r
  16.     """
    ; s1 d4 Z8 N2 _; m4 K  q  }
  17.     block={8 h+ W; o4 X2 J; [; |7 D
  18.     'index':len(self.chain)+1,7 A: U+ a) E- t: ?2 \7 T
  19.     'timestamp':time(),
    ( |6 t  a2 `0 J
  20.     'transactions':self.current_transactions,5 s1 F: o9 Z( n% U# ]8 R  `$ i& d
  21.     'proof':proof,: e3 ~# h7 R% }
  22.     'previous_hash':previous_hashorself.hash(self.chain[-1]),
      u" s5 E5 C/ R
  23.     }
    + h; @' H  {1 D
  24.     #Resetthecurrentlistoftransactions
    : J9 h* Q: b0 {5 U0 b
  25.     self.current_transactions=[]
    7 M( l3 }+ j+ H9 u4 \9 a: `! W
  26.     self.chain.append(block)
    : K2 Y, L' a$ A
  27.     returnblock. p! L* g7 D& i- n
  28.     defnew_transaction(self,sender,recipient,amount):5 D) H0 L% {3 g6 A
  29.     """! e8 J6 X- q0 Q2 ]6 Y  j
  30.     生成新的交易信息,信息将加入到下一个待挖的区块中, J8 A4 ^$ R3 W4 A, B9 l0 s+ g
  31.     :paramsender:AddressoftheSender/ [! t: T* k( ^7 O# v5 y) x
  32.     :paramrecipient:AddressoftheRecipient9 o1 d, q! L) d3 H8 W
  33.     :paramamount:Amount
    / ]4 I$ S  A1 k5 Q
  34.     :return:TheindexoftheBlockthatwillholdthistransaction4 u# r# P- K4 r$ p4 h
  35.     """
    $ l. ?9 l, b1 ~' @9 i6 M4 O3 r
  36.     self.current_transactions.append({
    9 a- q$ Z* m# p
  37.     'sender':sender,
    1 s- V! b# q; q0 u( e$ `( c
  38.     'recipient':recipient,) t: |* L. m2 {% x8 s% X
  39.     'amount':amount,
    5 p& L  P, j: g" q8 r8 _
  40.     })
    * L! F( a( y7 a6 k# p$ {
  41.     returnself.last_block['index']+1- O" H7 D0 }0 Z  K
  42.     @property! m2 l) `' F4 w7 H" }  X' j3 e
  43.     deflast_block(self):
    ! f2 M' {5 V4 \- ]( O# q
  44.     returnself.chain[-1]
    2 T, E8 s& b7 o* r( m/ ~. m3 H
  45.     @staticmethod# A! V0 l3 p! Y1 D% h) d9 b
  46.     defhash(block):' j! ]" U' I' L* {5 T, Z+ Y
  47.     """0 ?/ P& c& L6 S3 E* @! A7 g5 u
  48.     生成块的SHA-256hash值) l0 m+ A$ h/ e" Q5 s# V2 r$ r" v7 O
  49.     :paramblock:Block
    . c$ s( R) ?' R1 |: K- y: ]6 Z
  50.     :return:
    1 ^: [, s8 z: Z# ]! J
  51.     """
复制代码

, g( @! ]5 l& ~7 G    #WemustmakesurethattheDictionaryisOrdered,orwe'llhaveinconsistenthashes4 O  ^/ h1 V4 x% X1 Y
    block_string=json.dumps(block,sort_keys=True).encode()
6 G) a8 u2 U: s0 t- g    returnhashlib.sha256(block_string).hexdigest()
# d0 c5 \: G1 \" v; `) _# w; K    上述应该是非常直接的——我已经添加了一些注释和字符来帮助大家理解。当表达出我们的区块链时,我们也快要完成了。但现在,你肯定在疑惑新区块是如何被创建、伪造或是挖出的。
$ d. \! U4 x, ?    理解工作量证明
2 B+ v$ \9 d5 U7 ^) v    新的区块依赖工作量证明算法(PoW)来构造。PoW的目标是找出一个符合特定条件的数字,这个数字很难计算出来,但容易验证。这就是工作量证明的核心思想。
! W+ E' l2 S3 @$ _    为了方便理解,我们举个例子:
% y, v# ~& K1 G9 _3 n" X3 l+ M    假设一个整数x乘以另一个整数y的积的Hash值必须以0结尾,即hash(x*y)=ac23dc…0。设变量x=5,求y的值?用Python实现如下:2 M* ^- _: C" R' Y9 V
  1.   fromhashlibimportsha256
    : }. Q; o2 H& R5 I2 c/ F4 @3 k1 N
  2.     x=5! u% h! @3 E* H
  3.     y=0#y未知! j5 Z, t1 d2 |; J- l8 L
  4.     whilesha256(f'{x*y}'.encode()).hexdigest()[-1]!="0":
    ; D7 J' U7 z0 @8 I
  5.     y+=1
    0 N- I9 S6 J5 S$ {
  6.     print(f'Thesolutionisy={y}')
复制代码
9 n7 c8 A9 M' b# c1 i! ]+ [* ?% I
    结果y=21,因为:( P7 w7 b3 Z# q/ Y
    hash(5*21)=1253e9373e...5e3600155e860
8 u+ w: [+ ~$ J' b7 w    在比特币中,使用称为Hashcash的工作量证明算法,它和上面的问题很类似。矿工们为了争夺创建区块的权利而争相计算结果。通常,计算难度与目标字符串需要满足的特定字符的数量成正比,矿工算出结果后,会获得比特币奖励。
2 }% D5 u. y% O5 `( V    当然,在网络上非常容易验证这个结果。" R5 |8 S) d5 s; ]; S$ }1 @. u
    实现工作量证明
% {0 W7 E) J5 E& `; Z# V2 K    让我们来实现一个相似PoW算法,规则是:3 Z+ e- S. C# p" e9 i0 H' i+ v, `
    寻找一个数p,使得它与前一个区块的proof拼接成的字符串的Hash值以4个零开头。
3 Z4 F0 u& I- N7 y1 k  
  1. importhashlib
    / _: Q4 N% k% H( g  Y* k
  2.     importjson! `9 I( Q5 Q  r# `& w. n
  3.     fromtimeimporttime
    ) l+ ]& Q5 Q9 H$ S
  4.     fromuuidimportuuid49 Y/ Q: ]; v0 F- H) w. Z
  5.     classBlockchain(object):
    % C: q% x! X# U) H9 y0 i6 S/ ]( ^
  6.     ...
    8 [: H: F5 d+ |$ M9 E6 n8 F
  7.     defproof_of_work(self,last_proof):
    ( j) O. H" Z" b/ ?& E8 t
  8.     """3 j, h: g: d' Q3 R
  9.     简单的工作量证明:0 Q9 q* j0 r. ]5 W
  10.     -查找一个p'使得hash(pp')以4个0开头
    : f7 S7 b3 _+ M7 I9 g* N  r
  11.     -p是上一个块的证明,p'是当前的证明3 [" X* P; K$ v  s2 W6 C
  12.     :paramlast_proof:3 Y# e+ E1 w& X: |5 G5 x
  13.     :return:
    7 n; I; ~8 d) T9 J0 `- m7 b
  14.     """. _9 w5 g+ k7 L: d7 ^
  15.     proof=07 l3 Q/ p3 f, @0 O. x* W4 {
  16.     whileself.valid_proof(last_proof,proof)isFalse:
    / e- v3 A" j) r; r4 G: o# l+ B* I$ v
  17.     proof+=1
    + w+ [4 t1 m: ?- H# u% e( ^, t+ f1 x( w
  18.     returnproof
    $ |0 P; h: I8 J, o
  19.     @staticmethod
    6 M  c* C, `" V% R$ p; g6 ~
  20.     defvalid_proof(last_proof,proof):
    & ~6 V2 x  ^+ u! y4 T4 @2 @1 Z
  21.     """
    * d  T& q5 s! O% W8 q. b; g
  22.     验证证明:是否hash(last_proof,proof)以4个0开头?: X. k- x  o- O) t1 q) l
  23.     :paramlast_proof:PreviousProof$ k$ m. C) {: h& Q- h# g4 N6 g
  24.     :paramproof:CurrentProof) O9 a2 _8 Q6 _! f, R% m6 I$ f
  25.     :return:Trueifcorrect,Falseifnot.
    1 L" T1 J& f& z$ `
  26.     """
    ! H* A$ Z0 X) ?8 M' ?
  27.     guess=f'{last_proof}{proof}'.encode()& p" L+ y$ E% g0 }% j/ F
  28.     guess_hash=hashlib.sha256(guess).hexdigest()
    6 I; g  c, a) x  v4 ~% w0 v
  29.     returnguess_hash[:4]=="0000"1 J: ^; V: d/ h7 i1 C4 |0 t
  30.     ```
复制代码

# b/ k: ?4 Z8 X7 @3 D" d6 ~# |    衡量算法复杂度的办法是修改零开头的个数。使用4个来用于演示,你会发现多一个零都会大大增加计算出结果所需的时间。  ~% ?- \5 }/ [) H, _; D
    现在Blockchain类基本已经完成了,接下来使用HTTPrequests来进行交互。2 G5 y4 o: l% r: P8 i& x4 C
    ##二、BlockChain作为API接口% [0 A% o4 a! {( v5 F  E+ U5 o7 g8 ?
    我们将使用PythonFlask框架,这是一个轻量Web应用框架,它方便将网络请求映射到Python函数,现在我们来让Blockchain运行在基于Flaskweb上。) `$ q1 g: w- j" ~: q$ w
    我们将创建三个接口:
$ G$ ?8 Y* Y4 J+ |( @   
  1. ```/transactions/new```创建一个交易并添加到区块  |- `7 `6 L) R5 r& i0 _8 s
  2.     ```/mine```告诉服务器去挖掘新的区块1 }) e, [" b9 H/ Q5 K
  3.     ```/chain```返回整个区块链1 |/ Q+ C8 a  c( H" a/ K
  4.     ###创建节点9 e  m+ ^2 ]6 }# @
  5.     我们的Flask服务器将扮演区块链网络中的一个节点。我们先添加一些框架代码:
    ) p: X; A8 z: D! q- O% C
  6.     importhashlib
    7 k1 G3 H. _3 V8 C9 p+ o
  7.     importjson9 j5 a. H# F" J9 G% j- Z, ~- h
  8.     fromtextwrapimportdedent
      e; @( |8 c$ W  |
  9.     fromtimeimporttime
    ) ]7 ^6 q+ V; q! l) F
  10.     fromuuidimportuuid4$ w% s# c5 g0 j3 d8 P- f1 v* l
  11.     fromflaskimportFlask
    / b& V9 Q; w/ A4 b. s
  12.     classBlockchain(object):
    ( Y& T& ]! I; C( m" L% v
  13.     …* ?4 P% c  Y7 k
  14.     InstantiateourNode
    8 M# ?! F8 }  m8 ^5 j0 ^
  15.     app=Flask(name)
    / s, s" Q6 {0 M5 ?
  16.     Generateagloballyuniqueaddressforthisnode
    - S; e! i$ z/ R! l
  17.     node_identifier=str(uuid4()).replace(’-’,‘’)4 [2 W& K  O; p3 ^' ~' m5 [" u9 z
  18.     InstantiatetheBlockchain$ I. {/ C$ b  U9 p
  19.     blockchain=Blockchain()
    9 X8 H9 {$ Y. e$ f" O4 L6 @4 ~/ D% @
  20.     @app.route(’/mine’,methods=[‘GET’])& t, b* m8 k/ n0 C( E" d8 O4 g
  21.     defmine():
    ( ]' h8 {  ~: D2 m9 a* F
  22.     return“We’llmineanewBlock”
    , ]# e+ \0 A$ \. S' ~
  23.     @app.route(’/transactions/new’,methods=[‘POST’])
    & D6 f; M3 U1 g; E8 F! v& W
  24.     defnew_transaction():
    : x1 l# Z* O3 k; ?! D
  25.     return“We’lladdanewtransaction”0 g% w: E" ?6 @; O7 \+ {
  26.     @app.route(’/chain’,methods=[‘GET’]); J4 \' r  w! A7 T' ~" ^' G
  27.     deffull_chain():6 @7 w! K7 a6 q' ?7 {1 c
  28.     response={# I6 n6 S+ G5 m9 M) B2 u3 ?
  29.     ‘chain’:blockchain.chain,
    0 B, I( |* I0 S  a- ?/ ~& y
  30.     ‘length’:len(blockchain.chain),
    3 K% y3 g1 b  {0 Q1 B, H6 h  a
  31.     }
    ' S" @6 ^$ {9 W
  32.     returnjsonify(response),200  I: g' q3 d7 B2 e- `3 W
  33.     ifname==‘main’:
    1 ~, X- R8 L- S1 M  S8 D3 ?! z% ~
  34.     app.run(host=‘0.0.0.0’,port=5000)! n5 ?. }# i  p& @- a7 l
  35.     ```
    7 c$ G. O4 K. V! ?) Y. t
  36.     简单的说明一下以上代码:
    : j& T( r! [% s2 G% P' i' e) z
  37.     第15行:创建一个节点。在这里阅读更多关于Flask的东西。
      O. @5 m- _( _' ^6 U) B& G
  38.     第18行:为节点创建一个随机的名字。
    4 R% C$ j/ z) @
  39.     第21行:实例Blockchain类。* l" \' m( L* Z% d$ t1 s
  40.     第24–26行:创建/mineGET接口。
    3 ?' D" L( k+ _9 j
  41.     第28–30行:创建/transactions/newPOST接口,可以给接口发送交易数据。/ P$ J% b0 ^8 q5 e% H
  42.     第32–38行:创建/chain接口,返回整个区块链。
    ; d2 y) }# z( |& l
  43.     第40–41行:服务运行在端口5000上。& q7 M' ]0 Q# p
  44.     发送交易
    0 p& ^# e8 O! E" Y! I. v4 J
  45.     发送到节点的交易数据结构如下:: [' M% B2 {4 Q
  46.     {
    6 V8 M2 R' a/ i
  47.     "sender":"myaddress",
    0 U8 ^9 n6 x; U4 x0 j
  48.     "recipient":"someoneelse'saddress",6 _, q* Z5 @9 j1 `* i; k' B
  49.     "amount":5
    / P$ T. t6 @! w' c' i  a
  50.     }
    6 u/ @6 y' t" {
  51.     之前已经有添加交易的方法,基于接口来添加交易就很简单了:( W. S& C  h- a
  52.     importhashlib: Z, {2 r$ d7 u1 f6 y7 d
  53.     importjson9 e+ J" A+ u, ]  a
  54.     fromtextwrapimportdedent
    . q9 Y/ W, g& C% p) D3 W* x
  55.     fromtimeimporttime8 s' R0 A' f3 y0 j! B" m
  56.     fromuuidimportuuid4
    ) H0 z" I1 @6 o  n, f: ~0 I& J$ ~8 h
  57.     fromflaskimportFlask,jsonify,request
    . \8 g2 o; [$ V1 z6 Y; w5 t1 E! d  \
  58.     ...
    2 B0 W( N7 L/ }- S  k$ [) S
  59.     @app.route('/transactions/new',methods=['POST'])
    ; b8 y8 `  ]) }' Q
  60.     defnew_transaction():
    ; H" x; n! A1 I1 d- D! J6 A: }
  61.     values=request.get_json()$ x# I6 r3 g* Y+ O) T
  62.     #CheckthattherequiredfieldsareinthePOST'eddata) |: ^% K$ F" \  |2 Z& V$ D
  63.     required=['sender','recipient','amount']
    0 |+ F! L. K3 V$ V3 E2 j2 M5 s
  64.     ifnotall(kinvaluesforkinrequired):
    7 F8 \+ P& O" }3 C/ _2 o4 k
  65.     return'Missingvalues',4004 {. Z" g4 M5 ^4 O# m3 b
  66.     #CreateanewTransaction4 a' h" F$ k2 \* u  y9 k
  67.     index=blockchain.new_transaction(values['sender'],values['recipient'],values['amount']): x$ W0 F) o; P
  68.     response={'message':f'TransactionwillbeaddedtoBlock{index}'}
    0 ]0 N2 `0 }6 p: E( s, ^+ e
  69.     returnjsonify(response),2010 X0 O+ g; _. i) \
  70.     ```9 o6 v0 H$ T, V
  71.     -创建交易的方法-+ v6 I, q3 c$ x+ F' z
  72.     ###挖矿
    : L( o2 ]1 _, y
  73.     挖矿正是神奇所在,它很简单,做了一下三件事:
    2 C/ m  [' P+ d7 T3 N
  74.     计算工作量证明PoW1 U# k. z0 E0 a: D- a" |
  75.     通过新增一个交易授予矿工(自己)一个币
    , u9 r% b1 P  d; ^' q
  76.     构造新区块并将其添加到链中* `' P! N. g" [: N& Z( N) o
  77.     importhashlib
    ) {, f  `7 O* D) q' A6 _* u
  78.     importjson
    ! G  r$ F# `  \& G9 O$ ~, @( K
  79.     fromtimeimporttime6 m, J; R( c$ E% j9 l; m
  80.     fromuuidimportuuid4
    4 f0 E$ ?; ]2 k. N
  81.     fromflaskimportFlask,jsonify,request
    4 n- i7 b) x/ z8 c
  82.     …' I7 Z. O' @0 I9 b; N. g
  83.     @app.route(’/mine’,methods=[‘GET’])
    - c3 Y  {  ~8 p4 a: x& n+ A
  84.     defmine():$ m! ~2 r3 ]0 O2 Y
  85.     #Weruntheproofofworkalgorithmtogetthenextproof…
    " T% F  D2 u/ ]! y
  86.     last_block=blockchain.last_block+ [- D1 y" j) A7 d+ H4 [4 n
  87.     last_proof=last_block[‘proof’]
    # c: W2 g" q9 R
  88.     proof=blockchain.proof_of_work(last_proof)
    - W  v4 N7 K  V. O  X( U
  89.     #给工作量证明的节点提供奖励.
    3 |( W6 O: T. g% `. a- z) [
  90.     #发送者为"0"表明是新挖出的币.
    7 C' x% E* Y" E7 K2 M9 N9 W' \
  91.     blockchain.new_transaction(
    8 d% N! S  y: Z& J/ o+ f' ?
  92.     sender="0",: b& x0 A$ ~% j9 u7 h6 w8 Q
  93.     recipient=node_identifier,0 i- E1 w, P6 z: `4 c( u/ u, f
  94.     amount=1,
    / k4 p  F# J9 k, n5 w
  95.     )
    6 S! s% C4 G4 h/ o* I7 a$ ^
  96.     #ForgethenewBlockbyaddingittothechain& K& |9 E% U  ^( V# z6 B
  97.     previous_hash=blockchain.hash(last_block)
    $ D$ m, n  G" Z
  98.     block=blockchain.new_block(proof,previous_hash)
    3 \9 d# v  d0 u2 x) r0 Y7 F
  99.     response={
    0 f/ E7 \1 I  S7 C
  100.     'message':"NewBlockForged",  w& E- ^: f6 u
  101.     'index':block['index'],
    & w1 [$ P% q& \' R. h
  102.     'transactions':block['transactions'],
    , Q% R. M2 a  e. b! D
  103.     'proof':block['proof'],6 @8 s+ _# Y4 h
  104.     'previous_hash':block['previous_hash'],# P( o4 e9 j4 e6 `9 D
  105.     }% O8 j" n6 k% K+ |! `) ^' |
  106.     returnjsonify(response),200
    / ?, J1 _) Q6 Z  S) w6 G. K$ q* E# ?
  107.     ```
复制代码
. ^4 z2 ?& S' {& j* {& ~2 S9 E' i
    注意交易的接收者是我们自己的服务器节点,我们做的大部分工作都只是围绕Blockchain类方法进行交互。到此,我们的区块链就算完成了,我们来实际运行下。
" {% |* B$ i/ F9 V  ?7 B, s0 [! i3 A    三、运行区块链5 T# f" g1 G1 d( R) o7 o6 i" u
    你可以使用cURL或Postman去和API进行交互
# M. o) L' J5 g) e+ T    启动server:
* I* C$ \. g$ k/ g4 U; Q    $pythonblockchain.py! I& o- ^! ?; k; Q
    *Runningonhttp://127.0.0.1:5000/(PressCTRL+Ctoquit)  b8 n. ~2 A: K1 g4 O
    让我们通过发送GET请求到http://localhost:5000/mine来进行挖矿:/ D: [! J6 e" D/ Z! _2 N. a
    -使用Postman以创建一个GET请求-
( G) t: p3 [" u. M3 x    通过发送POST请求到http://localhost:5000/transactions/new,添加一个新交易:
4 f: c: Z# p) R. X6 A+ s    -使用Postman以创建一个POST请求-
5 U5 Z6 {8 v/ `6 ]4 V    如果不是使用Postman,则用一下的cURL语句也是一样的:
! \5 a- Y' i2 K# Z2 W" R$ V. M    $curl-XPOST-H"Content-Type:application/json"-d'{
: H& |7 J& p; C( Q/ T9 H( ]0 W    "sender":"d4ee26eee15148ee92c6cd394edd974e",; G& q+ b) O- e$ z( G  M; H
    "recipient":"someone-other-address",7 ^, Z# ]+ A6 }9 D6 W8 F
    "amount":5
  e2 {" M) a3 A    }'"http://localhost:5000/transactions/new"# `6 G8 M% ~; q; d
    在挖了两次矿之后,就有3个块了,通过请求http://localhost:5000/chain可以得到所有的块信息:0 o6 t: s2 _% C6 _
   
  1. {8 C% R* ~1 m5 F
  2.     "chain":[
    3 Y* S; r: ]- O7 d, I) i1 {
  3.     {
    $ n$ w" |. J! a+ Q& G) k
  4.     "index":1,
    1 T+ y' E5 a0 n
  5.     "previous_hash":1,
    0 ^  G* r/ k3 ?. t0 |) \4 }+ q8 K
  6.     "proof":100,
    ! h; E( _8 z% F0 o3 m+ X
  7.     "timestamp":1506280650.770839,9 {0 x' x1 g# F* x+ S: _) f; O
  8.     "transactions":[]
    : S* Y. [2 r. Z4 X6 ], s$ Q" V
  9.     },
    8 ?1 l/ d/ T" r3 M$ `
  10.     {# S& A$ i* `% d7 z
  11.     "index":2,$ d* o; S- N' l: a: I% \% `
  12.     "previous_hash":"c099bc...bfb7",. |* k) m, ]  S% J
  13.     "proof":35293,* |8 W) i" x* B; h9 F+ f7 i$ X
  14.     "timestamp":1506280664.717925,
    6 y. O) |4 u2 ~) Q
  15.     "transactions":[
    4 y: e, u7 d- [1 v) C+ y
  16.     {
    / F2 Z" u% J9 d2 X) P5 m! V; R" w
  17.     "amount":1,
    5 Y# c1 q# `# R) K7 Z
  18.     "recipient":"8bbcb347e0634905b0cac7955bae152b",+ I7 @7 ^) B6 X9 l7 {; |
  19.     "sender":"0"
      P( t# \) J) o6 I- ]) v$ K
  20.     }
    6 y0 G5 w- E) ~: K- E" l+ `
  21.     ]. y# ~; X" y6 t( g8 |; h4 i
  22.     },
    ! c# F% o# ], Y5 [6 k7 Y
  23.     {( ~( O! b1 {4 {, O9 h
  24.     "index":3,3 F; Q3 a' a4 H* K
  25.     "previous_hash":"eff91a...10f2",. G( b/ ^' c, {7 d* u
  26.     "proof":35089,
    # K% i6 e1 N' `
  27.     "timestamp":1506280666.1086972,' m( ?, c- U4 F5 g
  28.     "transactions":[# d7 O' k2 I& a) q
  29.     {/ h# `1 T9 Q7 R2 _; m
  30.     "amount":1,
    " e6 e8 Y- D# R: V
  31.     "recipient":"8bbcb347e0634905b0cac7955bae152b",
    , a2 d1 x) }8 Z9 L) z! Y" C+ N
  32.     "sender":"0"
    ) o5 z  |9 Q2 B8 J5 y8 a& `/ C
  33.     }' ~  y* U3 Z! O) u8 e* y/ l
  34.     ]
    + F% J2 n! x8 {; U! m$ k
  35.     }
    ( z" X0 J/ d9 {% K  @: {
  36.     ],( j$ Z# K) V+ R4 Y
  37.     "length":3# w# _# x. B; I1 c
  38.     }
复制代码
9 I4 Z! c! \3 [( a: T" D8 @8 M5 }
    四、一致性(共识)
1 W7 A1 k% v9 k: [2 g9 A0 }    非常棒,我们已经有了一个基本的区块链可以接受交易和挖矿。但是区块链系统应该是分布式的。既然是分布式的,那么我们究竟拿什么保证所有节点有同样的链呢?这就是一致性问题,我们要想在网络上有多个节点,就必须实现一个一致性的算法。
3 A! ^7 H4 O  c, E5 P6 B2 A    注册节点
4 ~. H0 s7 \# I    在实现一致性算法之前,我们需要找到一种方式让一个节点知道它相邻的节点。每个节点都需要保存一份包含网络中其它节点的记录。因此让我们新增几个接口:3 y) {, c1 b# {) \% F/ a& B
    /nodes/register接收URL形式的新节点列表
2 t6 q! n) q1 z; Y6 e  V; }5 w    /nodes/resolve执行一致性算法,解决任何冲突,确保节点拥有正确的链
0 U  x) V; d& O% c% a6 \! ^    我们修改下Blockchain的init函数并提供一个注册节点方法:8 Y) M7 p# m% w, [& m/ p$ b# }% }$ j
  
  1. ...
    ) m; I1 Y2 U9 r
  2.     fromurllib.parseimporturlparse
    % w0 a7 q8 T" j, F
  3.     ...
    ( u" B6 A: w0 Q9 v" a
  4.     classBlockchain(object):: L8 X7 @1 o* a' n9 X& e2 {
  5.     def__init__(self):0 ]5 m( I. M$ F% a7 J0 f
  6.     ...- J/ H1 r+ z# v! Z$ z' A# Y9 @
  7.     self.nodes=set()  w  D3 L3 C6 _5 S7 P$ l+ \& V
  8.     ..., Y% @3 _) B( I
  9.     defregister_node(self,address):
    4 Z8 c) a9 T( |2 l5 k
  10.     """# {+ @: `" L5 N
  11.     Addanewnodetothelistofnodes
    1 f+ `4 ?/ I( Q( w1 _. R6 W
  12.     :paramaddress:Addressofnode.Eg.'http://192.168.0.5:5000'
    , h; e& n8 p% Q( J' l  a+ c
  13.     :return:None* L* x6 _# F' G% S! F2 T: N
  14.     """; Y$ W5 x# y4 R. m
  15.     parsed_url=urlparse(address)% a, n! L; d0 @- g- w5 [
  16.     self.nodes.add(parsed_url.netloc)
    3 b2 ~0 b- t5 U4 _9 ~
  17.     ```1 I( [$ X' I8 y
  18.     我们用set()来储存节点,这是一种避免重复添加节点的简单方法——这意味着无论我们添加特定节点多少次,它实际上都只添加了一次。3 ]3 _0 V4 d7 Y
  19.     ###实现共识算法& s  `7 m. [: B
  20.     前面提到,冲突是指不同的节点拥有不同的链,为了解决这个问题,规定最长的、有效的链才是最终的链,换句话说,网络中有效最长链才是实际的链。我们使用以下的算法,来达到网络中的共识。
    * C" `/ B/ D6 c& R+ s3 R
  21.     …! a" b$ b; g  |9 A+ S2 `( U4 Y
  22.     importrequests$ _! E3 C- z/ f
  23.     classBlockchain(object)! P% z/ u& j+ x" Y2 _6 l& h
  24.     …/ A2 b4 `8 l9 Q% d
  25.     defvalid_chain(self,chain):
    4 B2 e, s; U" z6 q; ]
  26.     """) D* A. l3 {7 T& p5 A8 }
  27.     Determineifagivenblockchainisvalid
    4 Q% w+ }# i7 D% X
  28.     :paramchain:Ablockchain* z9 h+ x% F" O$ Z
  29.     :return:Trueifvalid,Falseifnot2 ~! J. r; V# w! G4 a
  30.     """* \8 m" L5 T! r
  31.     last_block=chain[0]) w5 w3 Q( c' Z% u( @, o( v% U
  32.     current_index=1  z7 [' E. o2 J+ ^4 Q
  33.     whilecurrent_indexmax_lengthandself.valid_chain(chain):$ l1 M4 U( F3 J
  34.     max_length=length
    9 u2 h5 W- J- Q8 k8 C
  35.     new_chain=chain, t' F8 d% ]- H( C: [. A
  36.     #Replaceourchainifwediscoveredanew,validchainlongerthanours) c1 ^# Y- z- k7 R  c( y- u# z
  37.     ifnew_chain:3 W3 t7 Z- Z+ O1 @
  38.     self.chain=new_chain
    % P* }4 O: J; B: x0 r
  39.     returnTrue
    ' a6 ?9 t$ ?& W* T9 v+ ~& F" ^4 ?
  40.     returnFalse: ~, {- h  q, q; O8 \
  41.     ```
复制代码
, J% G% t2 P6 r5 \% J
    第1个方法valid_chain()用来检查是否是有效链,遍历每个块验证hash和proof。: [" K5 \- [9 C' o0 a% _
    第2个方法resolve_conflicts()用来解决冲突,遍历所有的邻居节点,并用上一个方法检查链的有效性,如果发现有效更长链,就替换掉自己的链。
+ o; @: F1 D  w7 L    让我们添加两个路由,一个用来注册节点,一个用来解决冲突。8 F3 x" H1 M6 b
   
  1. @app.route('/nodes/register',methods=['POST'])
    0 Y( j/ y, Q, a: N" M6 ]
  2.     defregister_nodes():0 c% g- w3 O, z- G! w0 f
  3.     values=request.get_json()5 {/ p3 T9 ]! [' K
  4.     nodes=values.get('nodes')3 b) q+ F+ J4 l
  5.     ifnodesisNone:. J: |6 _( e$ |* @$ e$ l+ F
  6.     return"Error:Pleasesupplyavalidlistofnodes",4005 }3 w4 R& S0 F  l& F+ y) n
  7.     fornodeinnodes:7 k; f* A7 q+ b( R8 C
  8.     blockchain.register_node(node)
    6 H4 q1 A/ C6 N6 e  O- A
  9.     response={: W* l2 p2 Q& a' D: L# l0 r' A
  10.     'message':'Newnodeshavebeenadded',
    , W$ I1 w% }" B4 Y1 B% p. c' M4 y1 f! m
  11.     'total_nodes':list(blockchain.nodes),, g2 \( s) U1 K, s
  12.     }
    3 _( [! \( ]) [) M5 N# L
  13.     returnjsonify(response),2018 R* s9 g; R- g) G- u
  14.     @app.route('/nodes/resolve',methods=['GET'])! L& T) Q. g1 X  z* l: F* k! B! \
  15.     defconsensus():9 u. l* J! P! O
  16.     replaced=blockchain.resolve_conflicts(). S7 R0 |' \) a
  17.     ifreplaced:" ^5 Y5 C8 Y5 J- u+ c6 B" _& ~
  18.     response={4 d2 q+ |& j( y" O  P5 u6 ^) @7 ]
  19.     'message':'Ourchainwasreplaced',! C- A$ e7 ?3 Y5 k
  20.     'new_chain':blockchain.chain0 t; b2 A8 I  j5 d  z
  21.     }
    , r/ d3 Q9 [+ E8 ?8 |3 Z
  22.     else:$ y& \2 K0 s) @! ]  ^0 {' y
  23.     response={( F* F& h) [! o  g! Y& N3 J
  24.     'message':'Ourchainisauthoritative',
    / m! J& k3 i. b! [% ~% Q
  25.     'chain':blockchain.chain
    7 K# ?6 e- G6 g5 K1 g9 ~2 Z
  26.     }2 I% I; h' B* D# E6 W
  27.     returnjsonify(response),200; \3 U  X5 f& q) @9 B( P# p0 v( W
  28.     ```
复制代码
% `: Q3 V, a- c( k8 W3 m* p
    你可以在不同的机器运行节点,或在一台机机开启不同的网络端口来模拟多节点的网络,这里在同一台机器开启不同的端口演示,在不同的终端运行一下命令,就启动了两个节点:```http://localhost:5000```和```http://localhost:5001```。
" Y+ x& |% K. S4 Q4 L
$ T, n+ F4 V4 Y" @  q    -注册一个新的节点-: I( d% A5 P, \2 w
    然后在节点2上挖两个块,确保是更长的链,然后在节点1上访问GET/nodes/resolve,这时节点1的链会通过共识算法被节点2的链取代。
标签: Python
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

江左没浪 小学生
  • 粉丝

    18

  • 关注

    0

  • 主题

    7