Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

用 Python 从零开始创建区块链

江左没浪
456 3 0
我们都对比特币的崛起感到惊讶惊奇,并且想知道其背后的技术——区块链是如何实现的。, A- m; w6 G% w# x6 u1 h
    但是完全搞懂区块链并非易事,至少对我来讲是这样。我喜欢在实践中学习,通过写代码来学习技术会掌握得更牢固。通过构建一个区块链可以加深对区块链的理解。
8 a4 T4 p3 L! E3 D, v' |) u    准备工作+ F% J0 d5 o5 ~5 s% ^9 l2 h" J; Q
    我们知道区块链是由区块的记录构成的不可变、有序的链结构,记录可以是交易、文件或任何你想要的数据,重要的是它们是通过哈希值(hashes)链接起来的。
$ F  w% f8 [5 a& a. ]8 w8 p- @; Y    如果你不知道哈希值是什么,这里有一个解释。; Z- ]% d; j' ]  L
    这份指南的目标人群:阅读这篇文章,要求读者对Python有基本的了解,能编写基本的Python代码,并且需要对HTTP请求有基本的理解。
, [0 G/ F+ s, c: V    环境准备:确保已经安装Python3.6+,pip,Flask,requests。安装方法:( F" O" R6 Z* g: w
    pipinstallFlask==0.12.2requests==2.18.4
* p! Q9 ]: Z! V: `    同时还需要一个HTTP客户端,比如Postman,cURL或其它客户端。7 @9 d5 O+ a8 E  L- m
    一、开始创建BlockChain! C% ]; Q! Y3 _
    打开你最喜欢的文本编辑器或者IDE,比如PyCharm,新建一个文件blockchain.py,本文所有的代码都写在这一个文件中。如果你丢了它,你总是可以引用源代码。
" F# a$ G4 X/ w    BlockChain类+ O$ Y$ q& j6 F- d
    首先创建一个Blockchain类,在构造函数中创建了两个列表,一个用于储存区块链,一个用于储存交易。
' l  o8 ^; N$ f, J; Z6 c) W    以下是BlockChain类的框架:! n  T" o  ^/ i( V# }
  
  1. classBlockchain(object):4 n. B3 J& S/ ^4 j
  2.     def__init__(self):
    1 I4 y8 {  w9 K0 Q+ M# m0 t
  3.     self.chain=[]4 A4 @  Q( F, T; q- w
  4.     self.current_transactions=[]/ D& f( ~* e; X3 D; Q( `+ T; F
  5.     defnew_block(self):6 q* {+ i2 \/ _' [; `) m( _. G: V! A
  6.     #CreatesanewBlockandaddsittothechain2 E: C2 i$ ?  l
  7.     pass% X0 @+ i; O6 G$ l# A/ S1 p1 I: D3 z
  8.     defnew_transaction(self):
    + |; [5 f  u% L. j! f! r0 \
  9.     #Addsanewtransactiontothelistoftransactions
    & i( ~6 G$ h9 o% S* z
  10.     pass+ i2 d1 z4 M2 o8 {; w
  11.     @staticmethod* `2 ^: }4 g  T1 ~8 j/ z/ Y4 X
  12.     defhash(block):& A% D/ b- g1 Y) c
  13.     #HashesaBlock
    & F, P! ^& D. h5 q, W
  14.     pass' p: ^; B4 U4 f. y4 |$ |( j& ~
  15.     @property: @( o  W6 `: Y/ l& U7 c" ?: z- U+ y, A
  16.     deflast_block(self):
    5 Z5 V/ g6 w* U
  17.     #ReturnsthelastBlockinthechain% n4 u) P5 k: F3 Z+ p
  18.     pass
复制代码
# p( W# Z' M. T$ Z: q; P( Q
    -我们的区块链类的蓝图-- w2 }# ~* G: z. b  N5 q
    Blockchain类用来管理链条,它能存储交易,加入新块等,下面我们来进一步完善这些方法。# p- {) @  p4 b& h: n
    块结构
& @4 O- t5 s8 c5 y/ @# C4 [3 D+ a" T    每个区块包含属性:索引(index),Unix时间戳(timestamp),交易列表(transactions),工作量证明(稍后解释)以及前一个区块的Hash值。
" \" x. u# x! d3 z) {    以下是一个区块结构:8 ]) J% b9 S* ^- B9 [1 O4 _
   
  1. block={/ i6 u/ q7 r6 C. H
  2.     'index':1,. v4 i- K, b6 R: O$ r# z( `
  3.     'timestamp':1506057125.900785,9 m* A5 e6 J' @6 x5 [  f  n9 u4 G& w
  4.     'transactions':[8 y$ S; q6 }4 k: F
  5.     {
    ' r4 ^2 K* C& E8 ]' l
  6.     'sender':"8527147fe1f5426f9dd545de4b27ee00",
      @5 i$ Q; Y: D; A" E2 p" t
  7.     'recipient':"a77f5cdfa2934df3954a5c7c7da5df1f",( Y0 ?- z; Q, T
  8.     'amount':5,
    / q3 r  F% Y$ u) N1 _
  9.     }
    ( c$ B: {# c8 P1 f/ V2 Q8 V$ [
  10.     ],% e$ X. a/ e' d+ y, o
  11.     'proof':324984774000,
    # J" c; M" O1 G+ B
  12.     'previous_hash':"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
    ' v$ \0 g, k  n3 z% \$ P
  13.     }
复制代码
0 d; J$ W! z& w, E! ?% B
    -链上一个区块的例子-
. n+ p- I( S& B, C    到这里,区块链的概念就清楚了,每个新的区块都包含上一个区块的Hash,这是关键的一点,它保障了区块链不可变性。如果攻击者破坏了前面的某个区块,那么后面所有区块的Hash都会变得不正确。. {: m5 O: F* w+ K( n! e9 r6 Z- l
    确定这有用吗?嗯,你可以花点彻底了解它——这可是区块链背后的核心理念。( ~. f( y6 t' c/ [. e) B
    加入交易: E2 A3 }( m! w0 v7 s! S
    接下来我们需要添加一个交易,来完善下new_transaction()方法。4 e9 w, P+ q# |" ^
  
  1. classBlockchain(object):: X( w* L  L+ k1 v0 Y$ B' h: O
  2.     ...2 J, T5 b- b+ X( m' s+ \( k
  3.     defnew_transaction(self,sender,recipient,amount):3 n  ~* S6 S) s
  4.     """& Q9 y7 y; l2 M, O1 o" \
  5.     生成新交易信息,信息将加入到下一个待挖的区块中
    # N$ x3 A/ S. ~; M% V5 |# q, V
  6.     :paramsender:AddressoftheSender' s/ _/ w/ G! B# Y
  7.     :paramrecipient:AddressoftheRecipient
    $ ?' N$ V+ q1 Z6 E/ S8 T
  8.     :paramamount:Amount3 |: y" k0 J; P
  9.     :return:TheindexoftheBlockthatwillholdthistransaction, @; Q. `) T  U) h5 r
  10.     """
      \% p0 c# j7 F" T% M! z1 ?  H6 W0 l
  11.     self.current_transactions.append({
    : Z. Y5 P' B; W" B, m8 g8 _2 @
  12.     'sender':sender,6 J1 O( {1 E1 L& a$ p
  13.     'recipient':recipient,
    ' Y$ s8 D! o: t; R* S
  14.     'amount':amount,
    $ B- _* B5 M4 ~# ]- C
  15.     })
    4 z! Q; b, Y7 k$ z
  16.     returnself.last_block['index']+1
复制代码

7 c) Z0 g) y( N! R, R    new_transaction()方法向列表中添加一个交易记录,并返回该记录将被添加到的区块(下一个待挖掘的区块)的索引,等下在用户提交交易时会有用。
3 d; ~* O2 Q# P. b    创建区块3 Z7 P/ r3 O2 E" ]% H
    当Blockchain实例化后,我们需要构造一个创世块(没有前区块的第一个区块),并且给它加上一个“工作量证明”。
, ?, c* a8 k5 Z, A. R9 C5 ~    每个区块都需要经过工作量证明,俗称挖矿,稍后会继续讲解。# d3 r% _, B. |* @2 y, e/ f
    为了构造创世块,我们还需要完善new_block()、new_transaction()和hash()方法:
, \8 X" Q, `3 P. p) D  
  1. importhashlib
    + |! t& a) _+ i, M
  2.     importjson
    ) n4 t$ e0 f& P1 q$ J0 |* ~
  3.     fromtimeimporttime
    , {% O; z, N! S, q4 |0 w& j5 c
  4.     classBlockchain(object):: P9 x5 t5 t5 }$ H1 @
  5.     def__init__(self):1 y9 @% m  j/ U
  6.     self.current_transactions=[]; f- L& L$ b' h0 Z
  7.     self.chain=[]
    * c9 F, U: B  L+ p% _
  8.     #Createthegenesisblock; w7 J5 n- ^4 |4 ~* Y) H
  9.     self.new_block(previous_hash=1,proof=100)
    ' f9 q$ h: @* h5 m$ v
  10.     defnew_block(self,proof,previous_hash=None):- h$ W# v- |( d1 ~3 c: S
  11.     """
    6 I: u* ]3 M) i
  12.     生成新块
    ( V  i9 X4 o2 L# J$ F
  13.     :paramproof:TheproofgivenbytheProofofWorkalgorithm( R# b( |$ B# C4 U- O+ e/ g  W$ `, A
  14.     :paramprevious_hash:(Optional)HashofpreviousBlock% r5 S/ M1 K& T
  15.     :return:NewBlock3 q$ j3 x4 E! }$ [  B3 R+ @5 |7 V. e# X8 z
  16.     """' ?9 p0 P1 g8 ]* G# T. d- J
  17.     block={- z6 p5 ?6 _  L7 z* B: C; E8 a( T0 |
  18.     'index':len(self.chain)+1,$ [5 R. Q6 n$ y1 E# k5 ~+ h
  19.     'timestamp':time(),
    4 G- e3 S, G) c8 f
  20.     'transactions':self.current_transactions,
    0 O- V6 P' @. `2 w% T: L. o
  21.     'proof':proof,1 N- ]' U$ r3 l6 t! b- T
  22.     'previous_hash':previous_hashorself.hash(self.chain[-1]),- U: y$ r7 U* b% E* d* |! t# x
  23.     }6 |' c& e- H0 v4 y1 V" ]
  24.     #Resetthecurrentlistoftransactions0 n8 x* c4 _- p7 ?4 j$ V2 M/ _
  25.     self.current_transactions=[]
    3 A  p; f* P4 `+ w
  26.     self.chain.append(block)
    , J+ ^$ m" b9 }' |8 E4 R
  27.     returnblock
      }' ?8 b) Z: y6 f5 p: p* h
  28.     defnew_transaction(self,sender,recipient,amount):
    ) l# z  u7 ]5 {: m0 w2 b
  29.     """/ @7 u1 R! J7 A8 |7 ?/ B
  30.     生成新的交易信息,信息将加入到下一个待挖的区块中3 L: y; q3 P* l0 p
  31.     :paramsender:AddressoftheSender/ x2 @- S) c+ X* l4 |
  32.     :paramrecipient:AddressoftheRecipient
    7 Q6 P1 l, M, L$ C
  33.     :paramamount:Amount
    " D8 c6 L5 r. v0 r
  34.     :return:TheindexoftheBlockthatwillholdthistransaction! c/ h. H8 q5 G$ B9 m, y. G1 r: O
  35.     """
    & M2 K! b6 ?* z* R) q
  36.     self.current_transactions.append({
    ' m. Z# \* y) h3 F) E
  37.     'sender':sender,3 M4 l  D9 `( A! W0 s- f! j: ]3 u8 I5 _
  38.     'recipient':recipient,
    8 }2 i0 |7 t! a5 z. @+ t6 L
  39.     'amount':amount,
    " a$ o. |  K4 P$ Z  O! a
  40.     })
    / ~, a$ i5 p0 S& C$ r$ k+ q- Y# {
  41.     returnself.last_block['index']+1/ B4 d# f0 \/ ^' n
  42.     @property: p/ O0 U( {" v% ~* n2 K0 S. y
  43.     deflast_block(self):  G. N/ ]7 v9 n' u
  44.     returnself.chain[-1]% G$ M2 R4 i3 P  h1 c2 Q
  45.     @staticmethod
    ) G; Q4 L+ \' t$ T
  46.     defhash(block):% P, S3 `, |* Q' J5 ?& t4 r
  47.     """* ]  {# t6 b2 A, l
  48.     生成块的SHA-256hash值( {0 e( `' n; E8 G& \7 H( k
  49.     :paramblock:Block
    ! R" ?8 n6 D5 j/ v) K
  50.     :return:
    8 O7 O  m- R) B
  51.     """
复制代码
( \5 Q" ?7 r! Z9 m+ w1 Q
    #WemustmakesurethattheDictionaryisOrdered,orwe'llhaveinconsistenthashes( ?7 l0 w* p+ x6 K
    block_string=json.dumps(block,sort_keys=True).encode()
2 x7 {; c1 D' l3 B2 Q$ ]& W5 P    returnhashlib.sha256(block_string).hexdigest()7 ]4 u  N" L# u1 M6 F. Z& _
    上述应该是非常直接的——我已经添加了一些注释和字符来帮助大家理解。当表达出我们的区块链时,我们也快要完成了。但现在,你肯定在疑惑新区块是如何被创建、伪造或是挖出的。
6 M) Q- X8 q( T; O: O    理解工作量证明
0 e  W3 c, ^3 a$ o) J! S    新的区块依赖工作量证明算法(PoW)来构造。PoW的目标是找出一个符合特定条件的数字,这个数字很难计算出来,但容易验证。这就是工作量证明的核心思想。: D5 p0 J+ z! W5 h
    为了方便理解,我们举个例子:; E* K/ f* s1 j: t
    假设一个整数x乘以另一个整数y的积的Hash值必须以0结尾,即hash(x*y)=ac23dc…0。设变量x=5,求y的值?用Python实现如下:7 W- A9 t) J" C# H* A( c: E0 d' ?
  1.   fromhashlibimportsha256# U7 e4 d6 i" _/ F
  2.     x=5' j* L4 N$ C! V3 \$ }( @
  3.     y=0#y未知
    $ c* b# s4 f" ^* S: Y
  4.     whilesha256(f'{x*y}'.encode()).hexdigest()[-1]!="0":
    * N3 D' a" U$ {9 g( m& ]: X+ a; Z
  5.     y+=1, [+ r+ ^1 i% v, J" v4 W7 l; t
  6.     print(f'Thesolutionisy={y}')
复制代码

, c! L1 w% J- D1 T( J    结果y=21,因为:
  q. }+ X* U8 H1 l+ k8 |5 f    hash(5*21)=1253e9373e...5e3600155e8608 o9 }: S2 @1 D$ f5 A
    在比特币中,使用称为Hashcash的工作量证明算法,它和上面的问题很类似。矿工们为了争夺创建区块的权利而争相计算结果。通常,计算难度与目标字符串需要满足的特定字符的数量成正比,矿工算出结果后,会获得比特币奖励。% U) o9 Y7 J) V& v( c
    当然,在网络上非常容易验证这个结果。5 k8 p- t# q$ b, R; M
    实现工作量证明7 x0 R( X) Z: g0 y+ j. K: U) F$ B; D
    让我们来实现一个相似PoW算法,规则是:
% h5 [5 m1 |2 A0 p  k7 s/ B5 @    寻找一个数p,使得它与前一个区块的proof拼接成的字符串的Hash值以4个零开头。
+ s2 \: I. X% a3 j0 W9 ?! E% Q% r0 o8 O  
  1. importhashlib
    ! u$ G0 D+ f+ E- A! y9 R! ~1 |
  2.     importjson
    6 d+ ]! V5 f3 `# O8 U) ]
  3.     fromtimeimporttime
    ! Z4 Z2 @3 K2 d6 r4 T
  4.     fromuuidimportuuid4$ b4 o7 U& G( _1 |+ X$ x
  5.     classBlockchain(object):
    $ X' z/ m! Q0 R% w
  6.     ...& v1 R3 j3 k9 }1 i
  7.     defproof_of_work(self,last_proof):
    2 M  K9 c4 E# x+ y2 B
  8.     """0 p7 [9 p% ~. [
  9.     简单的工作量证明:3 y) A- ~9 g# f6 l. h
  10.     -查找一个p'使得hash(pp')以4个0开头& R' |; m5 b/ V( J9 s8 x0 B# \
  11.     -p是上一个块的证明,p'是当前的证明( M* b* K8 K7 u7 e( \: i
  12.     :paramlast_proof:
    ; E. N% y% x& N; ]7 ]* l+ D# E+ G
  13.     :return:
    2 d0 K1 ~  ?! E' M- {( \
  14.     """
    + ~4 X: \# i2 q% G8 R2 ~( d/ Y
  15.     proof=08 F  w' F9 ~! |$ n$ V
  16.     whileself.valid_proof(last_proof,proof)isFalse:3 R+ P" T7 s1 ]4 z
  17.     proof+=1  v5 V* @5 F& T+ ?$ y/ a' K) z4 J7 F
  18.     returnproof; ]: I! |. D' ]' D
  19.     @staticmethod7 L" b& v- k2 G, Q
  20.     defvalid_proof(last_proof,proof):& G* P' X2 D) k7 a! G( a5 G
  21.     """
      w! `) ^* \" E7 ?. n7 Q7 \4 L, f
  22.     验证证明:是否hash(last_proof,proof)以4个0开头?; T& M- b! H  h3 ^) J" f! l9 M
  23.     :paramlast_proof:PreviousProof5 o* W  Y4 q: b& q5 _, m  r
  24.     :paramproof:CurrentProof2 L. U5 k5 G  x" S
  25.     :return:Trueifcorrect,Falseifnot.: d( ^8 U# M2 e# K
  26.     """" H, |. a8 g" O0 r6 m/ P0 |
  27.     guess=f'{last_proof}{proof}'.encode()
    4 H" g9 x- d9 m0 r" _
  28.     guess_hash=hashlib.sha256(guess).hexdigest()
    * A+ J# ~2 K" J' s9 x* w+ C
  29.     returnguess_hash[:4]=="0000"$ j% P( k% U/ J1 |7 c/ L. @! ?
  30.     ```
复制代码

& d0 Z; y- m/ W) R( H; Z( _. U2 W    衡量算法复杂度的办法是修改零开头的个数。使用4个来用于演示,你会发现多一个零都会大大增加计算出结果所需的时间。
/ ^+ w. v1 G) i- [2 M    现在Blockchain类基本已经完成了,接下来使用HTTPrequests来进行交互。& I& z5 d1 Y+ M
    ##二、BlockChain作为API接口
/ Q7 \' k. J: L& q- F    我们将使用PythonFlask框架,这是一个轻量Web应用框架,它方便将网络请求映射到Python函数,现在我们来让Blockchain运行在基于Flaskweb上。( O) Y6 v9 a0 I& z3 |- P' H
    我们将创建三个接口:% ]. k5 O$ V: w; D
   
  1. ```/transactions/new```创建一个交易并添加到区块2 @' _& X* \. e) s) D
  2.     ```/mine```告诉服务器去挖掘新的区块
    * O5 O$ Q9 t6 j3 ?$ I+ {
  3.     ```/chain```返回整个区块链! H4 p$ E; d3 h) H0 }
  4.     ###创建节点$ B* A* |; z2 T6 m
  5.     我们的Flask服务器将扮演区块链网络中的一个节点。我们先添加一些框架代码:
    0 T& P/ R- c& i* ?9 F
  6.     importhashlib& k; }9 D3 V* L
  7.     importjson. y( Z( n# I1 \
  8.     fromtextwrapimportdedent
    0 i  C' p) Z8 m0 h
  9.     fromtimeimporttime
    ! @1 K  w6 G2 @2 p# h' q3 E5 p; E
  10.     fromuuidimportuuid4+ l# ^( s$ r. X" o) s; C, J
  11.     fromflaskimportFlask
    ( y$ ~- w9 s4 ?" a3 X
  12.     classBlockchain(object):
    - |: ]" i. `' k* i3 @6 \
  13.     …
    0 }, n% o3 R0 z8 i
  14.     InstantiateourNode+ n, T2 S' j1 A. h8 W' D
  15.     app=Flask(name)/ T% p# l# y- |7 N1 l1 ]8 ~5 O
  16.     Generateagloballyuniqueaddressforthisnode
    ' H  R2 z" B* _+ }: z# N
  17.     node_identifier=str(uuid4()).replace(’-’,‘’)3 Z' E# @# X2 _, R
  18.     InstantiatetheBlockchain) f! L1 L# C6 b4 G8 ~
  19.     blockchain=Blockchain()
    " E2 Z6 q5 P  [+ O# y- `: l$ a
  20.     @app.route(’/mine’,methods=[‘GET’])
    & e0 ], x- r& F0 o1 E
  21.     defmine():& g+ ]4 [& t( l* u6 p! Q
  22.     return“We’llmineanewBlock”- I! b) h0 M- j; R) _$ ]! s
  23.     @app.route(’/transactions/new’,methods=[‘POST’])
    ) a" p: a: L( Q; ~) A6 W9 M
  24.     defnew_transaction():
    . r7 B% X) N4 a# p$ F* }2 A
  25.     return“We’lladdanewtransaction”
    ' Y& A% r- o  H" a
  26.     @app.route(’/chain’,methods=[‘GET’])" C# ~5 u$ B) Y2 L& [
  27.     deffull_chain():9 n" c/ M$ V# n# [+ r
  28.     response={
    - d- N( i' }+ c/ N* \, X5 S
  29.     ‘chain’:blockchain.chain,
    / J/ _3 c1 q$ W- [
  30.     ‘length’:len(blockchain.chain),
    * k2 x) r/ Y3 Y; S% W2 Z+ l- R) I
  31.     }0 O" R* n2 d& M) V2 R* q) T
  32.     returnjsonify(response),200
    7 {7 g2 p/ E! g" v; F5 N( y  r# j
  33.     ifname==‘main’:9 k( u$ N* j9 s0 |  A* K+ A
  34.     app.run(host=‘0.0.0.0’,port=5000)$ F+ E$ k/ q. t8 T9 ^7 ]
  35.     ```9 P' i2 D; _9 d# a) Z! O" r% j
  36.     简单的说明一下以上代码:* \7 H/ W' M* `2 v
  37.     第15行:创建一个节点。在这里阅读更多关于Flask的东西。. h" C. _5 i# h. A2 x8 W! F
  38.     第18行:为节点创建一个随机的名字。' j4 ?1 ^: b. n2 {$ y) l
  39.     第21行:实例Blockchain类。8 l' g% f# ~- t/ {
  40.     第24–26行:创建/mineGET接口。+ Y7 ?/ d* Z& y. q$ w% W
  41.     第28–30行:创建/transactions/newPOST接口,可以给接口发送交易数据。- A; g7 v: ]  C( m5 j
  42.     第32–38行:创建/chain接口,返回整个区块链。
    6 x$ W6 R; V9 s& j
  43.     第40–41行:服务运行在端口5000上。
    % L3 y3 S2 @2 k9 h' T' D% }
  44.     发送交易
    5 B; t5 F- T6 h
  45.     发送到节点的交易数据结构如下:
    3 e7 M# Y% F$ L3 L- R
  46.     {
    8 \1 V, b9 J- @4 S* V0 d  O. Y3 D
  47.     "sender":"myaddress",
    8 ?) r* g- S# ]6 l( D
  48.     "recipient":"someoneelse'saddress",
    % \1 g' U8 j* e0 o7 B- E9 {
  49.     "amount":5( t% m; m8 V" }- J$ q! O; L
  50.     }, s! s" N& T- U1 v% L
  51.     之前已经有添加交易的方法,基于接口来添加交易就很简单了:
    9 m$ _: v2 {6 q% {8 l/ T
  52.     importhashlib
    7 ~: O0 l7 `: P1 r4 v: g6 v# @
  53.     importjson
    9 X  o* b: h" ]: P1 W6 u! v) {7 @
  54.     fromtextwrapimportdedent
    / A) }' ~" ^* `0 c0 F( c* @( k  s
  55.     fromtimeimporttime
    ! S$ B6 u9 J- e% {- N7 O  N! E. O2 p
  56.     fromuuidimportuuid40 ?0 Q1 X/ d3 o) T
  57.     fromflaskimportFlask,jsonify,request
    0 J# l- d, y* r# `( X  M$ [
  58.     ...
    4 C0 f; S; s( U
  59.     @app.route('/transactions/new',methods=['POST'])
    ( k  U6 m. l. ]! {
  60.     defnew_transaction():
    + R/ \' H, L- I, p
  61.     values=request.get_json()/ ~; q8 @! b8 M( T
  62.     #CheckthattherequiredfieldsareinthePOST'eddata4 Y: ^" `* R2 E" i
  63.     required=['sender','recipient','amount']" [( H5 y9 \- w" r
  64.     ifnotall(kinvaluesforkinrequired):
    1 o3 g0 c6 L2 V6 D
  65.     return'Missingvalues',400
    : {% D; I1 ?! U2 O
  66.     #CreateanewTransaction
    - D3 q& X2 w* V/ z* [5 t
  67.     index=blockchain.new_transaction(values['sender'],values['recipient'],values['amount'])" m/ g. [2 T% X7 \8 ^
  68.     response={'message':f'TransactionwillbeaddedtoBlock{index}'}9 O4 ?; i; F0 }& a
  69.     returnjsonify(response),201
    2 |% w; z5 K1 c  k' x
  70.     ```
    7 ?& W; ^2 q$ Q  Q  x6 z
  71.     -创建交易的方法-# r% T0 z7 x; k3 w  Y8 \
  72.     ###挖矿
    ; U- y2 s+ E5 c0 i
  73.     挖矿正是神奇所在,它很简单,做了一下三件事:
    . Y1 q! l, e, t
  74.     计算工作量证明PoW
    2 X: z: A( w) ]8 j: z8 G
  75.     通过新增一个交易授予矿工(自己)一个币
    ( c6 k; ?0 w% m$ \
  76.     构造新区块并将其添加到链中
    $ J& ^5 l* W3 p  H7 Q# T6 d
  77.     importhashlib
    % E' k- t5 W4 P; r+ C! E
  78.     importjson5 [: Z1 d, e+ Y5 S9 R
  79.     fromtimeimporttime
    # A# q/ e" t. A, T
  80.     fromuuidimportuuid4" m1 x& @0 |* T. C% P* Y2 o
  81.     fromflaskimportFlask,jsonify,request
    % t& f9 i' ?3 t/ G& R2 N# z
  82.     …* S# H) r% _  H, u, _! P/ P# h
  83.     @app.route(’/mine’,methods=[‘GET’])
    4 n8 `1 _3 l8 q$ Q4 A) U1 {
  84.     defmine():- _1 w* Q4 J  @3 h
  85.     #Weruntheproofofworkalgorithmtogetthenextproof…! `! e5 _4 \* T5 X6 @- P+ I
  86.     last_block=blockchain.last_block
    ( \1 m4 [" G5 Y5 ~/ n! t; n+ |; V
  87.     last_proof=last_block[‘proof’]9 r1 ]  g& V$ u( O) p; |1 ^+ Y6 k
  88.     proof=blockchain.proof_of_work(last_proof)! z! s- h& [' E% A6 p& g$ G
  89.     #给工作量证明的节点提供奖励.  S; U8 O; G; ~, y
  90.     #发送者为"0"表明是新挖出的币.. W% \! Q$ y7 _+ m  W2 H4 Y3 x
  91.     blockchain.new_transaction(! U" F1 w  v# V- R# t! Z
  92.     sender="0",$ s% G$ `' c0 T3 [$ ]' |1 Z, N
  93.     recipient=node_identifier,
    4 _- E9 G6 p% f* P
  94.     amount=1,
    5 d- e2 a; y, X. H9 B* P$ i
  95.     )# `$ o: v: K, b9 H9 d  M2 A
  96.     #ForgethenewBlockbyaddingittothechain* V; {8 i5 k' |5 _
  97.     previous_hash=blockchain.hash(last_block)
    ) S& _! D4 d( m6 a/ `
  98.     block=blockchain.new_block(proof,previous_hash)
    ' A! U6 B; x' L6 b# m3 ~
  99.     response={' Y( @, k" @5 ]8 A7 @
  100.     'message':"NewBlockForged",# O: m% n2 W& U) A* q! @
  101.     'index':block['index'],' c! e. }4 F+ o5 |  w/ X
  102.     'transactions':block['transactions'],9 w# t  H: l0 x  H1 i$ T6 C( J  R
  103.     'proof':block['proof'],0 x: |# w: `4 [& P: S0 }" @
  104.     'previous_hash':block['previous_hash'],8 q2 o: H8 _% S* y  E/ v
  105.     }8 E! C6 f; y. t, z/ {! e" |6 t% m
  106.     returnjsonify(response),200
    ; ]; {3 @1 p% d$ N5 n
  107.     ```
复制代码
+ r2 a: o% Z) T1 X, }- w
    注意交易的接收者是我们自己的服务器节点,我们做的大部分工作都只是围绕Blockchain类方法进行交互。到此,我们的区块链就算完成了,我们来实际运行下。
- o" r- W5 t6 n5 Y. i    三、运行区块链8 _7 i9 b) n  J. l8 Z
    你可以使用cURL或Postman去和API进行交互4 ?  @% _3 C" p
    启动server:# C7 V$ A* N3 s* F: R' O
    $pythonblockchain.py" S% M4 X& T& b
    *Runningonhttp://127.0.0.1:5000/(PressCTRL+Ctoquit)
0 E4 X9 U: e" p# F( Q  h: C    让我们通过发送GET请求到http://localhost:5000/mine来进行挖矿:
( h1 h4 ]! c0 }$ x6 r. S    -使用Postman以创建一个GET请求-
$ ]1 V5 L+ p6 F    通过发送POST请求到http://localhost:5000/transactions/new,添加一个新交易:3 Y. ~9 G+ H! d4 H  c
    -使用Postman以创建一个POST请求-
3 S' K2 L8 V1 ?/ E1 l; @7 J) I    如果不是使用Postman,则用一下的cURL语句也是一样的:. p& g3 t# O! f
    $curl-XPOST-H"Content-Type:application/json"-d'{
: h, a* D' q; \0 T2 q7 V' O, W    "sender":"d4ee26eee15148ee92c6cd394edd974e",
7 _# N$ y/ x% W    "recipient":"someone-other-address"," @$ ?+ j1 }. E8 d" x) {0 j  E
    "amount":5
0 w, ]2 S% r7 }6 Q: `* U    }'"http://localhost:5000/transactions/new"' t; _+ `; d  c
    在挖了两次矿之后,就有3个块了,通过请求http://localhost:5000/chain可以得到所有的块信息:
, S" ^  q' d% j  H% Z6 |- |   
  1. {# P1 E  \) R! s1 ]
  2.     "chain":[
    : v* I- R" D4 e- L0 n, R
  3.     {
    ( O0 G4 G8 w" {- h
  4.     "index":1,9 s, d% w  \4 I$ U
  5.     "previous_hash":1,* t: j% \7 O( R( @% w1 Z, H4 X
  6.     "proof":100,' f8 _3 k- `$ N* e* D
  7.     "timestamp":1506280650.770839,7 X+ {& m% b/ ?1 Y0 y
  8.     "transactions":[]( Z  G" W1 Y% y, y
  9.     },
    + M" p" A' [1 o9 u2 ?" R, w6 r- t
  10.     {6 `8 M$ H& g2 _; |  f  l; \2 F
  11.     "index":2,( R0 f7 d4 C# K5 S$ F' ]2 g
  12.     "previous_hash":"c099bc...bfb7",# r  r: G! S8 R. N+ \/ L
  13.     "proof":35293,$ Q8 c7 j+ G& X* T& p
  14.     "timestamp":1506280664.717925,  e. e6 b" f- s3 x  V( h* B
  15.     "transactions":[! [& s0 Q4 c3 g7 r8 b
  16.     {' i" n; s5 P: {- S4 P
  17.     "amount":1,' c# X# @" v, B: g. y8 G. k/ @! B7 Q. E' K
  18.     "recipient":"8bbcb347e0634905b0cac7955bae152b",8 t) f$ ^1 h* H8 p( h# S6 p* b
  19.     "sender":"0"/ X2 `) Y+ H/ F- k9 Q2 j6 @5 q- g9 Y5 M% r& A
  20.     }% i3 N, `  w$ c7 T' L: Y# y
  21.     ]
    3 t( J7 J5 H* l  o
  22.     },# x' l3 ?4 f4 f+ D6 u/ C& I
  23.     {
    4 j) j0 D4 X+ O* U$ ~/ Z+ B
  24.     "index":3,; ^2 O4 L6 x, `/ d% k' o; \
  25.     "previous_hash":"eff91a...10f2",! i. S+ T$ \3 k( X/ u0 [- Q5 X/ W# ?
  26.     "proof":35089,
    3 G. O* i- P0 V! v6 A, Z+ N* z. j
  27.     "timestamp":1506280666.1086972,
    % c& w  t( U+ L! e
  28.     "transactions":[
    : G; n6 \0 D$ {' t
  29.     {+ n# _# }. e, B. x; M
  30.     "amount":1,
    ) y7 F, Z! H2 F2 I% o& [" b4 l- R1 f
  31.     "recipient":"8bbcb347e0634905b0cac7955bae152b",6 D- K" ^5 ?4 s* V# _9 w/ z
  32.     "sender":"0"- s3 J( O5 r& g9 s. B, H
  33.     }6 Q3 \# Y0 ?4 T" D  J
  34.     ]
    ; \& a1 P# X' |7 A' k) C
  35.     }) O5 Y: n4 y! [: A
  36.     ],
    . [9 x% Z  m) q2 d
  37.     "length":3
    $ Y* i" t( T6 E0 {
  38.     }
复制代码
3 x! M+ V! M8 f! x( W. d1 Y
    四、一致性(共识)
& |! D) `" a+ \2 D- U3 F    非常棒,我们已经有了一个基本的区块链可以接受交易和挖矿。但是区块链系统应该是分布式的。既然是分布式的,那么我们究竟拿什么保证所有节点有同样的链呢?这就是一致性问题,我们要想在网络上有多个节点,就必须实现一个一致性的算法。& z% U7 Q( m# j" t) ^) l. r
    注册节点) I8 X3 ?8 n) q, W
    在实现一致性算法之前,我们需要找到一种方式让一个节点知道它相邻的节点。每个节点都需要保存一份包含网络中其它节点的记录。因此让我们新增几个接口:) ?- s; o0 m0 ~$ M0 u
    /nodes/register接收URL形式的新节点列表2 T# F) y, G9 n- a- }
    /nodes/resolve执行一致性算法,解决任何冲突,确保节点拥有正确的链* h6 y' d1 C" b7 w; T
    我们修改下Blockchain的init函数并提供一个注册节点方法:0 T6 C& F5 P  N3 `8 i
  
  1. ...
    ; E! z# E0 f6 C2 u8 U
  2.     fromurllib.parseimporturlparse
    . B' N+ A( s  u' c  r1 [
  3.     ...% \6 K6 F) @0 T) A& U2 A7 J0 ]  u
  4.     classBlockchain(object):$ a3 Q. q( _1 P7 _, s' b! a  n
  5.     def__init__(self):0 T( C: ~: t1 Z
  6.     ...( A7 A! {; x& i
  7.     self.nodes=set()% S- e# b* ^- h; @+ w$ X8 {' W
  8.     ...
    ! C- n3 q$ T/ }; U8 i& H1 z
  9.     defregister_node(self,address):0 r  S& j& D# f/ j" @7 k
  10.     """$ I* G# D7 @& u1 B% ]
  11.     Addanewnodetothelistofnodes) D) T3 ?7 S/ B3 L
  12.     :paramaddress:Addressofnode.Eg.'http://192.168.0.5:5000'
    9 `8 z) s+ f/ A1 E
  13.     :return:None
    - B7 g8 G/ _( f3 B
  14.     """
    2 ?! Q" a" Z% J! @( \& Z
  15.     parsed_url=urlparse(address)* h9 l% V( w- \' E
  16.     self.nodes.add(parsed_url.netloc)
    ' A6 L( [; ?2 E( {  A2 }
  17.     ```
    & V) L- O; E6 e& p2 n- E
  18.     我们用set()来储存节点,这是一种避免重复添加节点的简单方法——这意味着无论我们添加特定节点多少次,它实际上都只添加了一次。
    " x* i7 R4 Z: V7 h
  19.     ###实现共识算法
    5 y- y. k5 s+ Q. X4 c
  20.     前面提到,冲突是指不同的节点拥有不同的链,为了解决这个问题,规定最长的、有效的链才是最终的链,换句话说,网络中有效最长链才是实际的链。我们使用以下的算法,来达到网络中的共识。' {; S! C7 n0 f+ I9 v0 Y6 v2 N
  21.     …6 w: B" j& v+ F0 p
  22.     importrequests  }" N- t/ k2 ^* D
  23.     classBlockchain(object)
    . V% a  X4 `; g8 w3 i+ K
  24.     …
    , P% a2 p- w! Q- N! m0 `& t
  25.     defvalid_chain(self,chain):
    & ~$ l$ Q) O$ T
  26.     """! a9 V- `" H: l9 y1 O
  27.     Determineifagivenblockchainisvalid
    8 G& }& d, ], P: C
  28.     :paramchain:Ablockchain4 R+ Z5 N( d  M: ~
  29.     :return:Trueifvalid,Falseifnot
    7 X9 [% y% F5 ?3 |" ^
  30.     """
    0 s+ V. n; G% _& n, v
  31.     last_block=chain[0]! U( J- @: G# j) J* K
  32.     current_index=18 J2 a. Q' F$ T
  33.     whilecurrent_indexmax_lengthandself.valid_chain(chain):1 v. Y8 k" O) e
  34.     max_length=length) {! P! C7 s" P( ^7 u2 \# W
  35.     new_chain=chain3 O$ n/ f# x) ?8 x+ |
  36.     #Replaceourchainifwediscoveredanew,validchainlongerthanours
    . R' i. ~# T+ I( [* e8 a
  37.     ifnew_chain:6 x$ i3 n  B7 J" \. A9 k0 n
  38.     self.chain=new_chain
    % u; f- `# ^. L1 y3 [! u3 o8 w& v
  39.     returnTrue! G: V, S  g0 F* {0 H- ^! h
  40.     returnFalse1 h8 t! @6 v1 L/ @' y! R" {) D
  41.     ```
复制代码

7 b. I+ f7 R! {5 q) `0 J    第1个方法valid_chain()用来检查是否是有效链,遍历每个块验证hash和proof。3 V5 }4 g: d6 J! f1 k% @
    第2个方法resolve_conflicts()用来解决冲突,遍历所有的邻居节点,并用上一个方法检查链的有效性,如果发现有效更长链,就替换掉自己的链。8 N; y- [& d& }
    让我们添加两个路由,一个用来注册节点,一个用来解决冲突。& q3 l7 P8 k$ N5 g# V' H1 y( |% D
   
  1. @app.route('/nodes/register',methods=['POST'])
    7 H4 y; s. \2 q) n9 E; j7 C
  2.     defregister_nodes():& q) `2 r5 h0 Q# y
  3.     values=request.get_json()
    & ^9 R- Z8 Q, [, K" `
  4.     nodes=values.get('nodes')
    9 A" k4 G* R# J: I$ g  J* ~9 i: P' S
  5.     ifnodesisNone:. q3 K" W0 P* H; L
  6.     return"Error:Pleasesupplyavalidlistofnodes",400
    & ^1 }0 r/ E! l, ]  j
  7.     fornodeinnodes:/ K2 q( t0 c. m- _' p
  8.     blockchain.register_node(node)
    ! R, D' D- o% b: p5 j8 u# n
  9.     response={: p3 ~  e7 d- `( B
  10.     'message':'Newnodeshavebeenadded',) l. E0 ^5 d+ a8 e
  11.     'total_nodes':list(blockchain.nodes),
    & A' ^! K; W& I8 a' C
  12.     }0 [' E! {8 P& |; k6 ~
  13.     returnjsonify(response),201
    : _' G& I/ K6 b  x  ~
  14.     @app.route('/nodes/resolve',methods=['GET'])
    2 m: l/ S7 ^, N1 q* }
  15.     defconsensus():+ j1 D7 w9 T3 m! @* C0 E) A' U
  16.     replaced=blockchain.resolve_conflicts()# {0 |+ b9 X/ ]) i5 e
  17.     ifreplaced:0 W6 V' x6 d6 W3 ]# o
  18.     response={3 V% M: k% R+ V# ?) b! D
  19.     'message':'Ourchainwasreplaced',
    & u) ?! q3 w3 o, q9 g0 D
  20.     'new_chain':blockchain.chain/ ~1 L2 x" N2 q8 S  L
  21.     }: p- e' K1 e! b8 i& i, W" W
  22.     else:* S. S3 S. s* S+ b1 E5 C$ t6 B
  23.     response={
    4 T2 V' R  P+ C3 }  c, M
  24.     'message':'Ourchainisauthoritative',
    ) k7 T0 s0 Z0 K) P8 F! D5 v
  25.     'chain':blockchain.chain2 [! k7 M* V* n
  26.     }
    5 @; _+ M/ Q: \8 l4 a7 s2 M
  27.     returnjsonify(response),200* c, C& F7 [3 y( u; s
  28.     ```
复制代码
& `+ t" f9 Q  j6 L$ a5 Z: J: a
    你可以在不同的机器运行节点,或在一台机机开启不同的网络端口来模拟多节点的网络,这里在同一台机器开启不同的端口演示,在不同的终端运行一下命令,就启动了两个节点:```http://localhost:5000```和```http://localhost:5001```。
; R6 ]' r' N- w& ?2 ^, }
* ?: {/ h( I9 D6 F8 I5 D3 I8 d    -注册一个新的节点-
. |/ t% C# B/ L0 l9 v5 ~1 E( s    然后在节点2上挖两个块,确保是更长的链,然后在节点1上访问GET/nodes/resolve,这时节点1的链会通过共识算法被节点2的链取代。
标签: Python
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

江左没浪 小学生
  • 粉丝

    18

  • 关注

    0

  • 主题

    7