Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

用 Python 从零开始创建区块链

江左没浪
310 3 0
我们都对比特币的崛起感到惊讶惊奇,并且想知道其背后的技术——区块链是如何实现的。
5 y% A; g, ^" K5 x/ W    但是完全搞懂区块链并非易事,至少对我来讲是这样。我喜欢在实践中学习,通过写代码来学习技术会掌握得更牢固。通过构建一个区块链可以加深对区块链的理解。
" ?( Z$ V, o/ b$ R( j    准备工作
7 j, a/ }- y6 }    我们知道区块链是由区块的记录构成的不可变、有序的链结构,记录可以是交易、文件或任何你想要的数据,重要的是它们是通过哈希值(hashes)链接起来的。
7 b! M3 d" Y. `5 N3 M    如果你不知道哈希值是什么,这里有一个解释。
1 A5 ~0 S, D& H" s- F3 v+ y    这份指南的目标人群:阅读这篇文章,要求读者对Python有基本的了解,能编写基本的Python代码,并且需要对HTTP请求有基本的理解。' n7 ], H- D: \, _) U7 ]
    环境准备:确保已经安装Python3.6+,pip,Flask,requests。安装方法:0 A& U5 F5 ^& t3 }% j% D
    pipinstallFlask==0.12.2requests==2.18.4* F0 b( i6 |' e
    同时还需要一个HTTP客户端,比如Postman,cURL或其它客户端。( W( e, }' g9 |
    一、开始创建BlockChain
) F% [- A# l+ C( c3 v( b1 {    打开你最喜欢的文本编辑器或者IDE,比如PyCharm,新建一个文件blockchain.py,本文所有的代码都写在这一个文件中。如果你丢了它,你总是可以引用源代码。- K! e% ?/ Q- U3 k  ]
    BlockChain类
: w6 s. z* Y. y2 p    首先创建一个Blockchain类,在构造函数中创建了两个列表,一个用于储存区块链,一个用于储存交易。
& q; @5 o. g9 R    以下是BlockChain类的框架:
" _; ?9 p# T' [' s  
  1. classBlockchain(object):
    1 E4 n% v8 }' m, ^! l/ `7 W' V
  2.     def__init__(self):
    ( b2 {5 W0 X/ p  r  O) r
  3.     self.chain=[]7 R. h/ Y/ d' ]2 O$ P1 @
  4.     self.current_transactions=[]
    * K- B$ r! ?. I& ~. `* t" \
  5.     defnew_block(self):8 W+ b, P# @' v- ~6 h. K( D
  6.     #CreatesanewBlockandaddsittothechain
    - x$ `0 _* W5 Y9 `4 k" n
  7.     pass
    5 H" G! V5 h7 ~& g( P$ E8 s
  8.     defnew_transaction(self):
    9 s0 I5 I" E! w& u0 m$ p% D
  9.     #Addsanewtransactiontothelistoftransactions/ ?5 n. v" l. _$ I3 O, O* {
  10.     pass
    1 c5 d. |# G7 `' K& y
  11.     @staticmethod% m6 J# @/ K! C2 t
  12.     defhash(block):
    : f. W( `" y" s
  13.     #HashesaBlock6 C1 O, ]$ v  i, I# C! @  m
  14.     pass
    & H' j. h* S) X& A' l  B
  15.     @property
    7 h+ a6 W% D7 @/ L8 \
  16.     deflast_block(self):
    - a( I/ g. ]! A% a0 z/ x' W9 J9 {
  17.     #ReturnsthelastBlockinthechain0 \' T* C6 ?/ ?+ A+ w# }: f
  18.     pass
复制代码

8 T9 J% p7 u, L" Z    -我们的区块链类的蓝图-
& p% h# r# u. A& W    Blockchain类用来管理链条,它能存储交易,加入新块等,下面我们来进一步完善这些方法。
  P: o6 B, Y4 z9 j! c/ Z    块结构
8 `9 a) V' L; H% I, `% F    每个区块包含属性:索引(index),Unix时间戳(timestamp),交易列表(transactions),工作量证明(稍后解释)以及前一个区块的Hash值。3 O# e5 r2 Q$ w' _5 M$ b
    以下是一个区块结构:" }8 n( ?; Q# E& s, \- k( X9 Q
   
  1. block={
      J5 u7 s, s; j4 j- C# ~9 M
  2.     'index':1,. a3 |4 m4 i" {1 E' \; B+ q
  3.     'timestamp':1506057125.900785,( |$ _! H2 q2 y) b$ y$ [
  4.     'transactions':[) m1 X0 n' g( J% j; d
  5.     {
    : i' E: W, C+ K5 t# q8 A: T
  6.     'sender':"8527147fe1f5426f9dd545de4b27ee00",
    . }: w- O2 T( e- s4 b
  7.     'recipient':"a77f5cdfa2934df3954a5c7c7da5df1f",
    6 @5 g) {4 Q- T2 C
  8.     'amount':5,9 r2 [, t6 N! w1 P& B# X; Z
  9.     }
    * y% B" }3 W$ E# q0 l+ B" ^) `
  10.     ],
    * I2 X: e+ `( K. S3 _; B
  11.     'proof':324984774000,* ~. c5 I7 J$ Z: l) c5 O
  12.     'previous_hash':"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
    5 E/ K! B0 e1 u' \2 k- I1 T9 ?( h
  13.     }
复制代码

5 s$ z# g4 g7 L! |/ ]" g    -链上一个区块的例子-' @9 P6 q. T* W- [
    到这里,区块链的概念就清楚了,每个新的区块都包含上一个区块的Hash,这是关键的一点,它保障了区块链不可变性。如果攻击者破坏了前面的某个区块,那么后面所有区块的Hash都会变得不正确。
$ H6 D) E6 i$ j  q  Y    确定这有用吗?嗯,你可以花点彻底了解它——这可是区块链背后的核心理念。
4 k! G- l3 c  z, [6 a' W    加入交易6 d4 Z( R0 \; }2 p2 H3 M
    接下来我们需要添加一个交易,来完善下new_transaction()方法。
9 y' N: O; [7 ~  
  1. classBlockchain(object):
    ( ^3 Q9 B: r1 D0 e$ D
  2.     ...# P; R* j. t( f$ v8 e' ^
  3.     defnew_transaction(self,sender,recipient,amount):8 N# u3 Q3 V& c- c* M+ y/ g
  4.     """
    3 i, Z1 R$ ^8 \+ E# g  A
  5.     生成新交易信息,信息将加入到下一个待挖的区块中4 Y+ c3 h2 J% y
  6.     :paramsender:AddressoftheSender
    5 ]7 C' s1 M/ R4 j' x0 s/ ?; n
  7.     :paramrecipient:AddressoftheRecipient
    * m0 d) U! ~$ j- R: x2 F
  8.     :paramamount:Amount
    / E  s+ r' A( |8 \2 q0 h
  9.     :return:TheindexoftheBlockthatwillholdthistransaction
    5 _- E. [/ u- }6 s. L
  10.     """3 c; C  K* n0 [$ W  v) I: ]( _
  11.     self.current_transactions.append({
    6 j: O9 C4 T0 S7 }- Q1 I
  12.     'sender':sender,
    % @3 k. p, L! i% a5 a
  13.     'recipient':recipient,8 u+ L' r1 |% L+ e1 X  Z! O, b
  14.     'amount':amount,
    3 p9 g0 n+ _1 K8 t/ ]( r" C6 `$ v: ^
  15.     })2 Q# O/ j' u; x* F8 Y( H$ P
  16.     returnself.last_block['index']+1
复制代码
( N) U) ?+ F2 W4 ^
    new_transaction()方法向列表中添加一个交易记录,并返回该记录将被添加到的区块(下一个待挖掘的区块)的索引,等下在用户提交交易时会有用。' W8 T# z$ v, L9 ~7 \) @
    创建区块5 {) y3 W- v( }* n* t. `
    当Blockchain实例化后,我们需要构造一个创世块(没有前区块的第一个区块),并且给它加上一个“工作量证明”。* ?; \  N' v0 n( L% X; S
    每个区块都需要经过工作量证明,俗称挖矿,稍后会继续讲解。1 B$ A1 P. ~- p$ [2 J
    为了构造创世块,我们还需要完善new_block()、new_transaction()和hash()方法:
0 d2 V' Y. X) o+ @, x, M  
  1. importhashlib
    ) H, ]. u) W1 z* h1 {3 w" C: D
  2.     importjson0 A, ^9 O3 X1 u& z! R7 w
  3.     fromtimeimporttime  x9 \6 @" S% p2 u7 F% r9 b
  4.     classBlockchain(object):
    9 c: ]8 {. e" F8 R, J1 B7 u
  5.     def__init__(self):
    ' n4 \! w( Y( f0 S# s
  6.     self.current_transactions=[]# R2 {; [/ j: X" _
  7.     self.chain=[]
    0 E! E9 N1 q9 _& I* H5 h( ~) \9 ^
  8.     #Createthegenesisblock; c5 X6 x# p: J) }3 J- r
  9.     self.new_block(previous_hash=1,proof=100)- H' b( A' F/ R; \, s
  10.     defnew_block(self,proof,previous_hash=None):
    ( V% A) S3 B' ~/ ?+ j/ u
  11.     """
    ( s, f  h3 }! B. q% N* K
  12.     生成新块( o4 `6 V: Z+ C& G. z' ^9 C0 V/ ~3 o
  13.     :paramproof:TheproofgivenbytheProofofWorkalgorithm
    # u7 |. Y$ v1 O$ p- E
  14.     :paramprevious_hash:(Optional)HashofpreviousBlock
    % e" N7 q" u. h4 S" Y
  15.     :return:NewBlock
    6 ], ]" }! d3 L. N  c4 V. Q' p: a
  16.     """/ m+ k, h) j& H( O5 p4 l2 M
  17.     block={/ K3 n& U% [' a% [9 b' Q! T
  18.     'index':len(self.chain)+1,
    2 L7 M0 D; d: s5 V: D
  19.     'timestamp':time(),8 u8 O3 K& J6 R& ?3 i0 }
  20.     'transactions':self.current_transactions,( V5 a  U$ ~0 v
  21.     'proof':proof,
    7 {# ]  o2 i2 c
  22.     'previous_hash':previous_hashorself.hash(self.chain[-1]),
    & Y! O) i6 C  }/ V0 O
  23.     }% e+ N' Z, @* w; {& K. r, I
  24.     #Resetthecurrentlistoftransactions! z, |4 q; G# t' ]8 D( w& V/ t
  25.     self.current_transactions=[]
    ; F/ w3 C% U( h" c* E# m
  26.     self.chain.append(block)
    5 Z" `! q. h4 ^$ r. s7 Q0 N
  27.     returnblock+ ~& ^$ C& h& D* S! v3 o
  28.     defnew_transaction(self,sender,recipient,amount):
    . ~8 ^, a9 n+ S
  29.     """) P% F  x* P) ]) {' h& d
  30.     生成新的交易信息,信息将加入到下一个待挖的区块中9 a& W" f3 D0 [1 o7 A3 f- f
  31.     :paramsender:AddressoftheSender
    ) }; f. J) O1 o) {- ?0 p3 V* G  A
  32.     :paramrecipient:AddressoftheRecipient5 g5 J; M, z" K% X" ^
  33.     :paramamount:Amount9 f$ M6 [/ z* s! D
  34.     :return:TheindexoftheBlockthatwillholdthistransaction
    , ]' E5 F% [# i+ Q
  35.     """
    . F, n$ L  z6 B* l( q; l2 m
  36.     self.current_transactions.append({, E4 l7 F( V  z: J: U3 s
  37.     'sender':sender,6 |7 K" N: F( a, w, e. f2 I
  38.     'recipient':recipient,$ ~" [: c; `% c; P0 A! w4 \
  39.     'amount':amount,
    $ D0 G2 m" N0 U; Z* i' P- }
  40.     })1 n8 O  w. M& X- r' n' x
  41.     returnself.last_block['index']+10 Z) J9 `7 |/ F
  42.     @property' H4 Q" f4 v8 W* R6 Z6 u$ m
  43.     deflast_block(self):
    , [/ L; v' g0 _3 M- \5 Z' h
  44.     returnself.chain[-1]
    % y) c( A, _: [$ ~
  45.     @staticmethod
    8 |5 t+ Q8 n( j9 s9 T1 l& l
  46.     defhash(block):9 ^  F( @) x  N' T3 y, w% t. ]# _
  47.     """' t7 r% g# R) q9 b# p/ x
  48.     生成块的SHA-256hash值, }: |; \; M8 c; y
  49.     :paramblock:Block& }' |' @$ J- H/ `2 W6 H- v; k5 v
  50.     :return:
    6 p$ f4 H8 b& q, _' _/ w( h4 t5 m  t
  51.     """
复制代码

* Y0 i; y  `" s; p    #WemustmakesurethattheDictionaryisOrdered,orwe'llhaveinconsistenthashes
4 Y: ]: w/ @4 [    block_string=json.dumps(block,sort_keys=True).encode()! E/ p" A0 |  B& u" j% J9 F
    returnhashlib.sha256(block_string).hexdigest()
7 B9 Q6 _9 v& C+ t    上述应该是非常直接的——我已经添加了一些注释和字符来帮助大家理解。当表达出我们的区块链时,我们也快要完成了。但现在,你肯定在疑惑新区块是如何被创建、伪造或是挖出的。
* L4 ?( a3 D. _: i. G    理解工作量证明+ U0 }- k+ o1 w) ~3 [
    新的区块依赖工作量证明算法(PoW)来构造。PoW的目标是找出一个符合特定条件的数字,这个数字很难计算出来,但容易验证。这就是工作量证明的核心思想。
3 \5 c7 F7 C  Y1 ?  h/ Y& R    为了方便理解,我们举个例子:! Z( M9 z1 ~( n' i1 H
    假设一个整数x乘以另一个整数y的积的Hash值必须以0结尾,即hash(x*y)=ac23dc…0。设变量x=5,求y的值?用Python实现如下:( o, `+ [- x3 c. c" Y
  1.   fromhashlibimportsha256
    * p9 q' v1 b) q8 T  W! U7 h* C
  2.     x=5
    $ ?# T; Y/ A* g6 T" h+ e) h% L
  3.     y=0#y未知/ _- `( n* c& K4 q$ n
  4.     whilesha256(f'{x*y}'.encode()).hexdigest()[-1]!="0":
    9 A$ U- B/ o: G& V' U1 S
  5.     y+=1" A4 D5 D6 Y- D+ k" T8 V( L& H
  6.     print(f'Thesolutionisy={y}')
复制代码
- j" F! y: f3 F5 W0 I5 r
    结果y=21,因为:6 Q! Q+ [0 a& w- {
    hash(5*21)=1253e9373e...5e3600155e860
  G; {/ `3 o2 }- [( ~7 ?" a    在比特币中,使用称为Hashcash的工作量证明算法,它和上面的问题很类似。矿工们为了争夺创建区块的权利而争相计算结果。通常,计算难度与目标字符串需要满足的特定字符的数量成正比,矿工算出结果后,会获得比特币奖励。* u" z' w; E, ^5 n% \- _% `1 F
    当然,在网络上非常容易验证这个结果。4 k1 \6 C) r5 L
    实现工作量证明+ C% L+ ?9 f1 }* M( U) ?
    让我们来实现一个相似PoW算法,规则是:
6 h: u# {1 M) L3 ]) l: M+ }9 {5 R    寻找一个数p,使得它与前一个区块的proof拼接成的字符串的Hash值以4个零开头。* p) ?" P) o: E9 [+ a/ r
  
  1. importhashlib
    . Q* D" x; {% z& n+ y& E9 X# }
  2.     importjson
    ( P$ E# t1 o) _. q
  3.     fromtimeimporttime8 m: n% q$ x" {, H
  4.     fromuuidimportuuid4! `0 |5 {+ K8 ^$ E, p/ @6 R; m
  5.     classBlockchain(object):2 y8 Y6 y5 a1 P/ Q3 s
  6.     ...
    0 K  l: Z* R- d5 \
  7.     defproof_of_work(self,last_proof):( W1 [& Z/ V# J9 I$ ]
  8.     """
    2 O& e! {# A2 ^$ \0 {% _
  9.     简单的工作量证明:7 S+ b4 _$ j2 A6 J6 X3 c. c
  10.     -查找一个p'使得hash(pp')以4个0开头
    . Q; `" M1 |8 d8 |  [
  11.     -p是上一个块的证明,p'是当前的证明
    8 `, e: ^& a- ~/ a' `! R
  12.     :paramlast_proof:2 `% s. P" z3 c6 O
  13.     :return:
    * Z" n, i* y( L- a) o
  14.     """3 O- K& h0 s) n  u$ f# t* S
  15.     proof=0% l& M) d3 {3 w
  16.     whileself.valid_proof(last_proof,proof)isFalse:
    " t$ X4 J! }9 e8 u2 O! g
  17.     proof+=1# Y2 }8 Y, r9 ~  q  A$ S  s
  18.     returnproof5 t& w* H$ B( h6 K) W
  19.     @staticmethod
    8 t& o8 `- \# r+ E/ _7 j* s
  20.     defvalid_proof(last_proof,proof):
    ' H6 l0 _2 _8 y5 B
  21.     """
    % \0 N# r( X8 P( G- K0 a
  22.     验证证明:是否hash(last_proof,proof)以4个0开头?3 h0 y6 m! B- R* e- k
  23.     :paramlast_proof:PreviousProof1 y3 H: b" ~% r7 L4 C) J" s* y: {
  24.     :paramproof:CurrentProof
    . y. o! h& }) i8 s2 A
  25.     :return:Trueifcorrect,Falseifnot.
    . e) v: S7 m/ g2 e9 u/ i; D- f
  26.     """
    # O% m; j/ _% X8 ?. M
  27.     guess=f'{last_proof}{proof}'.encode()! f. o. {, X- ]' \, \
  28.     guess_hash=hashlib.sha256(guess).hexdigest()$ j" ^2 e1 W: g; J3 R
  29.     returnguess_hash[:4]=="0000"8 s/ S5 U+ K% p& a, J. V
  30.     ```
复制代码
- U; x' j% S' k4 Z6 Y
    衡量算法复杂度的办法是修改零开头的个数。使用4个来用于演示,你会发现多一个零都会大大增加计算出结果所需的时间。+ v& o; [% I7 w8 Z; v3 w
    现在Blockchain类基本已经完成了,接下来使用HTTPrequests来进行交互。
: j1 H, S! a' \4 ~7 q    ##二、BlockChain作为API接口
0 ^' e, L: U3 k    我们将使用PythonFlask框架,这是一个轻量Web应用框架,它方便将网络请求映射到Python函数,现在我们来让Blockchain运行在基于Flaskweb上。' o% w2 |+ o0 w
    我们将创建三个接口:1 A& Z- K5 T+ _3 \9 \# z0 }
   
  1. ```/transactions/new```创建一个交易并添加到区块
    & v2 H; C8 H8 p- C, `( j
  2.     ```/mine```告诉服务器去挖掘新的区块  d: g6 t1 }! Z* w2 c
  3.     ```/chain```返回整个区块链
      `' X) p0 H( [8 n% W7 V& W
  4.     ###创建节点. T& [6 Q* j- ]8 |
  5.     我们的Flask服务器将扮演区块链网络中的一个节点。我们先添加一些框架代码:9 q) s: K  I$ f0 C4 P$ v5 e. v
  6.     importhashlib
    ( Z8 |* F! i4 `
  7.     importjson
    8 {5 z; C) G$ ]  h; f  Y
  8.     fromtextwrapimportdedent+ V/ c5 {/ X% q4 @- X
  9.     fromtimeimporttime0 m+ J+ L4 k) s% b8 J. Z+ C7 r4 i
  10.     fromuuidimportuuid4- a1 @0 z! i, K% I" J
  11.     fromflaskimportFlask( K; A4 {% b: J5 l/ g: n. G
  12.     classBlockchain(object):, Y  M0 i4 t9 j
  13.     …' }* ?+ O3 u- }6 t8 _7 ~" B
  14.     InstantiateourNode! p5 C; j( x* i* G9 [) ^' i5 F! J
  15.     app=Flask(name)
    7 p: B! |/ q3 O% l
  16.     Generateagloballyuniqueaddressforthisnode- S  |$ l7 B+ @: x7 k, B
  17.     node_identifier=str(uuid4()).replace(’-’,‘’)
    & n, }8 H0 B+ w
  18.     InstantiatetheBlockchain" I/ y, U1 e8 |6 R( U
  19.     blockchain=Blockchain()
    ' A( \- W! I% f1 k3 i0 {
  20.     @app.route(’/mine’,methods=[‘GET’])
    6 v5 P  _, m: J
  21.     defmine():
    - ~) E! W- ]  y4 ?- z" j' E
  22.     return“We’llmineanewBlock”5 W. e3 ~& a: l6 {8 W  b
  23.     @app.route(’/transactions/new’,methods=[‘POST’])
    + ?9 F: V3 `( h3 y6 k% O5 ^4 x2 x% s
  24.     defnew_transaction():
    6 S1 P8 E1 m4 c- [' x
  25.     return“We’lladdanewtransaction”2 V( F3 p0 k: j
  26.     @app.route(’/chain’,methods=[‘GET’])
    . H" D& Z0 N' _+ W0 k6 K3 f1 f
  27.     deffull_chain():
    / ~7 w9 C* \8 c
  28.     response={
    ( w- N1 `, _' e" ^5 A$ r& A
  29.     ‘chain’:blockchain.chain,
    0 e( D- ^8 U8 ~% C8 l' m
  30.     ‘length’:len(blockchain.chain),. S) o( B+ A  ]3 H5 v! r) I
  31.     }) T5 w9 c1 ^3 Q# w& ]4 ]( k' q0 q$ o0 l
  32.     returnjsonify(response),200
    3 h, W! L  V* j6 e! P- u
  33.     ifname==‘main’:8 ~; E2 }4 x, I! F; p9 O, R( j% U
  34.     app.run(host=‘0.0.0.0’,port=5000)
    & W( U2 d; U4 B5 Z8 E  b- _
  35.     ```
    % S; W" f8 v8 x; b- _) B  g  e& i
  36.     简单的说明一下以上代码:
    0 B% }3 f- r1 p. |+ A1 X* V$ }
  37.     第15行:创建一个节点。在这里阅读更多关于Flask的东西。
    . x& C; c  i: t/ D" M) ~
  38.     第18行:为节点创建一个随机的名字。
    - R5 y1 Y+ M( ?" N8 h
  39.     第21行:实例Blockchain类。* O$ O, t5 r! q* c* Q
  40.     第24–26行:创建/mineGET接口。) y1 w2 _! r' E
  41.     第28–30行:创建/transactions/newPOST接口,可以给接口发送交易数据。( w' @6 {' x3 R
  42.     第32–38行:创建/chain接口,返回整个区块链。* r% _: M; Y# p4 s6 T- C
  43.     第40–41行:服务运行在端口5000上。
    5 I; i3 r& |8 a8 t8 m  `9 V* |
  44.     发送交易
    ( O8 u# k* H, H
  45.     发送到节点的交易数据结构如下:
    " @. Z9 D) k' l
  46.     {' t$ _( e& G4 x3 B
  47.     "sender":"myaddress",0 I# |& x3 D0 n, N- O4 @
  48.     "recipient":"someoneelse'saddress",
    ; ~7 ~' G6 B4 R( E, P& H
  49.     "amount":5/ c, L5 h3 A# i/ h* x( s& E
  50.     }% a( U8 D" k2 {+ S+ r, h; c
  51.     之前已经有添加交易的方法,基于接口来添加交易就很简单了:& b- r4 f8 L. J6 H9 \3 V
  52.     importhashlib' S- ^1 H# h, P. w" z. S" R- D
  53.     importjson" C" H- w! i; G3 {4 |: Y/ V
  54.     fromtextwrapimportdedent8 K) m, v' M) |$ I5 K
  55.     fromtimeimporttime
    4 c9 C7 x$ w& w8 v2 k' D+ s2 i( L
  56.     fromuuidimportuuid4
    6 V% I6 v6 H2 p& n5 \0 T
  57.     fromflaskimportFlask,jsonify,request
    * R: H# O6 X& N
  58.     ..., l9 k  Y3 t* i- e4 K$ k, v# r% M
  59.     @app.route('/transactions/new',methods=['POST'])- F1 e$ E: }! ~! @2 L+ n" n
  60.     defnew_transaction():
    / c6 x. k& g& t- ?
  61.     values=request.get_json()
    2 n. b, z; ]4 z0 L9 E
  62.     #CheckthattherequiredfieldsareinthePOST'eddata) O) G$ ~5 r) j% N$ w
  63.     required=['sender','recipient','amount']9 Y1 H. p5 z. k
  64.     ifnotall(kinvaluesforkinrequired):
    % e5 T4 ?6 M7 Y  Y
  65.     return'Missingvalues',400  s: j; W5 w" O* m6 g5 M8 o/ ~/ E
  66.     #CreateanewTransaction
    8 Y  e% e( O" ]) |7 C" z
  67.     index=blockchain.new_transaction(values['sender'],values['recipient'],values['amount'])) z, P$ _) N- G4 W# B' {6 B
  68.     response={'message':f'TransactionwillbeaddedtoBlock{index}'}
    9 I3 r! V" c4 m% a% x
  69.     returnjsonify(response),201
    * _. B$ J& M" x) M% n
  70.     ```
    9 B2 N% D* H. z' W7 `/ Y4 I3 i; P9 g; |
  71.     -创建交易的方法-% e8 A9 b2 B$ t% D
  72.     ###挖矿
    % a- X  X3 Q! O
  73.     挖矿正是神奇所在,它很简单,做了一下三件事:
    / X2 r. Z' w3 H) F5 s' i
  74.     计算工作量证明PoW0 c6 D$ Y4 V1 R; y# D  W
  75.     通过新增一个交易授予矿工(自己)一个币
      O* H9 [- M- ~1 R2 N9 V& k
  76.     构造新区块并将其添加到链中
    4 Z8 M9 V2 g  s; ]: q- C+ g
  77.     importhashlib; y" s6 y# |+ F* C
  78.     importjson5 q- r6 P. {: n) q0 t
  79.     fromtimeimporttime
    & g" ~) T, m3 O$ U
  80.     fromuuidimportuuid4
    / o2 d( _% f0 i
  81.     fromflaskimportFlask,jsonify,request+ b3 W3 I7 O: _
  82.     …2 x8 G: _1 c' V
  83.     @app.route(’/mine’,methods=[‘GET’])
    9 z$ y- E# F& q# B# Q
  84.     defmine():
    ' \: V0 I( y7 K5 A3 b0 U
  85.     #Weruntheproofofworkalgorithmtogetthenextproof…
    8 D$ ~5 a% M! E5 t! y: L/ Y' p4 ^
  86.     last_block=blockchain.last_block) B# d7 A8 _  p( R1 O9 K
  87.     last_proof=last_block[‘proof’]6 r8 o% Q6 v2 H. |" k
  88.     proof=blockchain.proof_of_work(last_proof)
    % R) `5 O* V  [/ p# R
  89.     #给工作量证明的节点提供奖励.
    1 p7 d2 Z+ j/ W% x2 G3 r: `
  90.     #发送者为"0"表明是新挖出的币.2 O& y" |$ Q! u8 _
  91.     blockchain.new_transaction(8 r; ]2 M2 O, o9 E* {
  92.     sender="0",
    ; i# Y$ @: f* r3 y0 Z9 y) t% n; _+ I
  93.     recipient=node_identifier,3 |3 C& ~) A7 k9 x! {. m
  94.     amount=1,( f+ ^! b4 K# _1 q2 C4 W
  95.     )
    2 ^/ i3 }+ n7 c
  96.     #ForgethenewBlockbyaddingittothechain5 U  |2 b8 @/ N
  97.     previous_hash=blockchain.hash(last_block)
    $ c9 P  e( m6 @+ j+ C% s4 |
  98.     block=blockchain.new_block(proof,previous_hash). i4 ~7 m' X# t
  99.     response={
    . T& Q2 Z/ U! {: D* \" S5 y
  100.     'message':"NewBlockForged",# z0 C5 {7 K  b; E0 V
  101.     'index':block['index'],
    - g6 C6 N" i! d  ~, L
  102.     'transactions':block['transactions'],
    * D& p; u* p! P
  103.     'proof':block['proof'],
    , @2 s+ W: u3 L% `1 |* h
  104.     'previous_hash':block['previous_hash'],5 S" r- T. b( B# k& @9 M. _# V- v
  105.     }
    " j7 U8 }+ C3 j- \# M
  106.     returnjsonify(response),2009 c. d4 r( J- @' n9 d: C% A( O3 X$ u
  107.     ```
复制代码

* g5 a6 T2 v* N$ J# K  s    注意交易的接收者是我们自己的服务器节点,我们做的大部分工作都只是围绕Blockchain类方法进行交互。到此,我们的区块链就算完成了,我们来实际运行下。" [, e+ [9 C/ S# |9 H, j& M( C$ x# V
    三、运行区块链# ?. f* C, k& \7 J8 [  j
    你可以使用cURL或Postman去和API进行交互( t2 g; Y2 ]" @6 D
    启动server:
. s9 W. x9 y" N: r: ?    $pythonblockchain.py
1 y# G4 M! g! M( {; {0 [2 q* i' b! e    *Runningonhttp://127.0.0.1:5000/(PressCTRL+Ctoquit)4 K2 V" Q4 k  z% `( {: A+ S
    让我们通过发送GET请求到http://localhost:5000/mine来进行挖矿:
8 H$ ?$ G! p* q    -使用Postman以创建一个GET请求-
$ A5 f, T  E- T: ^1 C% W    通过发送POST请求到http://localhost:5000/transactions/new,添加一个新交易:
  Y$ F9 ?/ l/ {- P9 L    -使用Postman以创建一个POST请求-
2 \# y% e2 [2 S$ x! Y' c1 z    如果不是使用Postman,则用一下的cURL语句也是一样的:
/ F2 |5 \6 }" B% X$ J1 ?    $curl-XPOST-H"Content-Type:application/json"-d'{
, N/ q1 V- A& ]& X3 D/ T& J    "sender":"d4ee26eee15148ee92c6cd394edd974e",; b. w( P3 d: a* Q7 l
    "recipient":"someone-other-address",2 x, p- V8 y. w* v- i: Y6 r2 s5 B( j# [/ `
    "amount":5
/ o& n" o) I+ F& f$ M% c- y" q    }'"http://localhost:5000/transactions/new"$ u* U' |5 N3 z  c% ?  i  l+ U, g
    在挖了两次矿之后,就有3个块了,通过请求http://localhost:5000/chain可以得到所有的块信息:4 L4 i$ Z$ B6 b( [" {
   
  1. {" ~3 H( L3 ^$ `2 l1 L6 D
  2.     "chain":[8 U  |7 E8 U; ^+ U" M2 q' [5 l
  3.     {4 s, z8 g' s, }7 M1 ^0 R- `. `1 E
  4.     "index":1,' ]; Y# _& z# u& ~; y' I
  5.     "previous_hash":1,
    : j1 S4 ^. S: G; d
  6.     "proof":100,
    0 f5 [! P1 t7 R7 P% o0 L
  7.     "timestamp":1506280650.770839,' |1 |, y, ~$ \2 Q+ p7 ~$ v
  8.     "transactions":[]
    1 S7 [: y0 s3 x& m2 z  z
  9.     },
    / |3 U% }- `# {+ W8 H0 n
  10.     {5 y- O1 ^9 K4 r) |3 S0 Y
  11.     "index":2,
    & F( U+ w6 M* R6 e( a, E) d
  12.     "previous_hash":"c099bc...bfb7",
      w3 O) Y3 k! `! ~1 x# ^, U
  13.     "proof":35293,( W+ ~) w* V( Y  z+ d; V, [2 {" ~
  14.     "timestamp":1506280664.717925,; p7 Z( Y1 R: B# ?9 h8 F
  15.     "transactions":[
    4 H0 X. U% ]$ m- F: {) K5 p
  16.     {- j+ @7 z( W/ N( A
  17.     "amount":1,# d/ c. \, _: S9 N/ A
  18.     "recipient":"8bbcb347e0634905b0cac7955bae152b",
    3 A5 _5 d& Z# P4 p1 H' O1 O
  19.     "sender":"0"& b8 \2 U% Z4 L4 U( d% U
  20.     }) I( Z5 F7 \7 w- }
  21.     ]
    2 K8 D& h% i4 V4 N7 }( n$ Q- @/ q
  22.     },# A/ ?! n& W" A* F% b' k
  23.     {0 s0 l. ^3 Y7 W4 C2 s9 j
  24.     "index":3,
    + n" e, ]$ B2 F2 R! F0 z
  25.     "previous_hash":"eff91a...10f2",5 }* S& x) E1 t4 L* e
  26.     "proof":35089,
    0 P# }# `0 ]7 C
  27.     "timestamp":1506280666.1086972,
    " i# I4 L5 v2 z0 K9 U/ ]+ M
  28.     "transactions":[" ^' i4 \9 n4 P6 R+ |% v9 q6 `: M
  29.     {( a8 |: x& Z& Q; H# o
  30.     "amount":1,
    2 p+ B7 y  Q2 f+ L; b1 q: {8 }5 f
  31.     "recipient":"8bbcb347e0634905b0cac7955bae152b",
    + m8 b$ e0 `1 \! j/ ~
  32.     "sender":"0"
    & x; B0 u6 _  U3 |4 I/ h) X" e4 [. f
  33.     }
    " |/ S; E6 K& D* o
  34.     ]
    / W9 w# v- ]3 R
  35.     }% j1 o  Q6 c5 |0 U
  36.     ],
    ( l' ?4 m2 ?: D. u2 T. g% G
  37.     "length":3. P$ y7 k( i" Y! g$ e
  38.     }
复制代码

& u- Q: K9 m9 c; R  m    四、一致性(共识)) N' t' h9 u! Q+ E7 V3 i$ u# Q
    非常棒,我们已经有了一个基本的区块链可以接受交易和挖矿。但是区块链系统应该是分布式的。既然是分布式的,那么我们究竟拿什么保证所有节点有同样的链呢?这就是一致性问题,我们要想在网络上有多个节点,就必须实现一个一致性的算法。5 C: r6 G; ^/ `& I: Y
    注册节点$ Y. H+ c9 ~: U* P. f. Z! _
    在实现一致性算法之前,我们需要找到一种方式让一个节点知道它相邻的节点。每个节点都需要保存一份包含网络中其它节点的记录。因此让我们新增几个接口:
4 w; m& N' V2 x- n6 G- R- A6 V" o- O/ W    /nodes/register接收URL形式的新节点列表
0 ]0 [9 [% G0 K5 U4 f    /nodes/resolve执行一致性算法,解决任何冲突,确保节点拥有正确的链
/ a; R! K5 E/ f6 V1 s    我们修改下Blockchain的init函数并提供一个注册节点方法:
8 \3 o$ E* ?: u8 P. W: y$ u* g  
  1. ...
    " y- |* ]7 A5 Q
  2.     fromurllib.parseimporturlparse2 E, c- m3 r* @( R  g
  3.     ...
    0 m* i1 k7 `; g1 B  ^; b
  4.     classBlockchain(object):/ [5 Z: X8 {, g& F2 S# g+ q
  5.     def__init__(self):
    0 }/ l2 _5 F4 F! s! ?* y) S
  6.     ...2 ~( E0 u+ e6 ^# N+ B
  7.     self.nodes=set()
    # ], a5 ~2 f6 s! w3 ?6 W
  8.     ...9 I$ z" t+ S& I2 e; f  Q
  9.     defregister_node(self,address):/ \) f) v2 a/ G9 W& L2 k" x
  10.     """/ t& s9 r' Z& f7 O# u% _
  11.     Addanewnodetothelistofnodes
    2 n' ~, q( E: @; x9 x
  12.     :paramaddress:Addressofnode.Eg.'http://192.168.0.5:5000') Z, c5 s: E) c  I5 L1 g5 G& ^
  13.     :return:None1 U' S# O* V. G8 U4 n
  14.     """4 ~1 U! u5 l2 ~. j- }$ x& |
  15.     parsed_url=urlparse(address). E) e, b/ `4 U% ?$ {: y$ q% Y
  16.     self.nodes.add(parsed_url.netloc)' b+ f+ H$ {3 f  X- _0 a! m( x, }
  17.     ```4 W! ^( M1 }) d' ~4 w
  18.     我们用set()来储存节点,这是一种避免重复添加节点的简单方法——这意味着无论我们添加特定节点多少次,它实际上都只添加了一次。
    3 q3 i$ z! u) ~# R8 m% }9 m( a
  19.     ###实现共识算法
    2 K! X- R9 u6 a9 t8 {
  20.     前面提到,冲突是指不同的节点拥有不同的链,为了解决这个问题,规定最长的、有效的链才是最终的链,换句话说,网络中有效最长链才是实际的链。我们使用以下的算法,来达到网络中的共识。
    & Q* [8 N& E* f- w3 h
  21.     …& t6 N" o  r8 ~: J  q" U
  22.     importrequests: v% ]$ W1 Q% x5 d# {9 ^6 c
  23.     classBlockchain(object)
    & X5 \; _# P$ V+ y5 M/ b/ }
  24.     …
    . r- z6 s0 g% V& C8 ]1 d7 _
  25.     defvalid_chain(self,chain):
    7 v1 f# k% q8 ?; K& a
  26.     """
    6 U6 D" L! @4 h& h! A1 v. K
  27.     Determineifagivenblockchainisvalid
    % m1 _6 I' Q/ u8 z- B
  28.     :paramchain:Ablockchain
    * c- Y% i4 h; {4 s
  29.     :return:Trueifvalid,Falseifnot1 m- {, z4 L: P( @
  30.     """
    ' |! i* h9 u# d- I8 q2 S, c; M
  31.     last_block=chain[0]
      v0 d( g' r4 O6 J& f
  32.     current_index=15 j0 R: R, ]0 Z: {7 R+ A3 \
  33.     whilecurrent_indexmax_lengthandself.valid_chain(chain):; S+ E8 R5 w- G5 m" A4 R
  34.     max_length=length% F5 I  h2 ?0 n4 o
  35.     new_chain=chain3 ]& A: a+ W/ j4 E' M
  36.     #Replaceourchainifwediscoveredanew,validchainlongerthanours. z' \$ S1 q9 W7 R# [+ M
  37.     ifnew_chain:& N9 f6 m8 @9 G
  38.     self.chain=new_chain
    & z# g& v9 C% x5 a) b% N
  39.     returnTrue0 Q1 C  q4 H, x; O
  40.     returnFalse1 ^( A  ^( ?' Q, O5 P
  41.     ```
复制代码
) S, c" M! y4 D$ ?8 w- P
    第1个方法valid_chain()用来检查是否是有效链,遍历每个块验证hash和proof。
7 I& k9 R% h) O- b  E    第2个方法resolve_conflicts()用来解决冲突,遍历所有的邻居节点,并用上一个方法检查链的有效性,如果发现有效更长链,就替换掉自己的链。9 L6 \/ o1 t) g
    让我们添加两个路由,一个用来注册节点,一个用来解决冲突。
4 m6 E; U2 P- N3 u( y   
  1. @app.route('/nodes/register',methods=['POST'])
    ( N- S7 T; e! t
  2.     defregister_nodes():. Q. M5 }# ^, V. y: }+ Q
  3.     values=request.get_json()8 ]8 ]( g7 F- ?  Y
  4.     nodes=values.get('nodes')
    * H3 {- v2 X  R0 z" [/ J; }3 P
  5.     ifnodesisNone:( `' o/ y7 i+ j
  6.     return"Error:Pleasesupplyavalidlistofnodes",4007 O8 ?) A9 t1 g; Q$ N
  7.     fornodeinnodes:! A; e: I: ^; m3 x0 P' \1 s) a. ]" k& ?$ u
  8.     blockchain.register_node(node)8 G' N; B7 j) j3 e) g8 S
  9.     response={# v# D" C) A7 @1 w/ @" W0 [
  10.     'message':'Newnodeshavebeenadded',# e& I# ~, M5 q) L" x8 i2 H
  11.     'total_nodes':list(blockchain.nodes),$ T9 @4 b1 m3 G9 S4 F; z
  12.     }
    % O( d3 b& y) u3 o+ E& |8 F; b& e: Q
  13.     returnjsonify(response),201% f) U6 F4 h$ [: m
  14.     @app.route('/nodes/resolve',methods=['GET'])
    9 i# X$ [- x" S+ L
  15.     defconsensus():5 [) s& l$ P4 g4 ~
  16.     replaced=blockchain.resolve_conflicts()
    + i4 T1 n- w; a  z( s/ |
  17.     ifreplaced:
    ; f! s/ k/ x3 F" ^3 o
  18.     response={
    ' {+ W' t1 i2 Z! B. G5 S  Y
  19.     'message':'Ourchainwasreplaced',
    ' M  g7 F1 h& D4 y
  20.     'new_chain':blockchain.chain
    7 ~% Y! c) ^; n' v% T3 @1 u
  21.     }
    ' l# P1 i8 y: F, o
  22.     else:
    % p! @. m5 z- z
  23.     response={, [$ Y$ k5 ?, b  o2 k: D
  24.     'message':'Ourchainisauthoritative',
    : B5 A7 W: W9 @6 ~% Y2 P8 c( ~
  25.     'chain':blockchain.chain+ d: _- m7 s. l3 I$ W, U
  26.     }
    2 j. `5 n; F+ _0 V: z
  27.     returnjsonify(response),200
    ! V. @) R$ ^# y  V( Y
  28.     ```
复制代码

% K- Y: N1 l9 h3 M, V; t    你可以在不同的机器运行节点,或在一台机机开启不同的网络端口来模拟多节点的网络,这里在同一台机器开启不同的端口演示,在不同的终端运行一下命令,就启动了两个节点:```http://localhost:5000```和```http://localhost:5001```。
& W2 w3 Q2 `+ m * o* F5 Y! K+ N% a( x. Y( k2 n2 x
    -注册一个新的节点-
3 ?0 L7 z; s. _+ O5 u    然后在节点2上挖两个块,确保是更长的链,然后在节点1上访问GET/nodes/resolve,这时节点1的链会通过共识算法被节点2的链取代。
标签: Python
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

江左没浪 小学生
  • 粉丝

    18

  • 关注

    0

  • 主题

    7