但是完全搞懂区块链并非易事,至少对我来讲是这样。我喜欢在实践中学习,通过写代码来学习技术会掌握得更牢固。通过构建一个区块链可以加深对区块链的理解。4 z. c7 g" M" _; H6 ?3 ^& _
准备工作! B/ g8 d1 k7 G1 W
我们知道区块链是由区块的记录构成的不可变、有序的链结构,记录可以是交易、文件或任何你想要的数据,重要的是它们是通过哈希值(hashes)链接起来的。
如果你不知道哈希值是什么,这里有一个解释。: r" ~3 Y3 U0 _) o( E. s) w$ y
这份指南的目标人群:阅读这篇文章,要求读者对Python有基本的了解,能编写基本的Python代码,并且需要对HTTP请求有基本的理解。
环境准备:确保已经安装Python3.6+,pip,Flask,requests。安装方法:$ n; R, s, [% p
pipinstallFlask==0.12.2requests==2.18.4. q( L. D9 }4 T
同时还需要一个HTTP客户端,比如Postman,cURL或其它客户端。
一、开始创建BlockChain
打开你最喜欢的文本编辑器或者IDE,比如PyCharm,新建一个文件blockchain.py,本文所有的代码都写在这一个文件中。如果你丢了它,你总是可以引用源代码。) [- @2 B- n4 ]5 k; P" n( v- s; A
BlockChain类
首先创建一个Blockchain类,在构造函数中创建了两个列表,一个用于储存区块链,一个用于储存交易。
以下是BlockChain类的框架:
- classBlockchain(object):
- def__init__(self):
- self.chain=[]$ D' W3 @. u: g3 t0 P4 i
- self.current_transactions=[]
- defnew_block(self):
- #CreatesanewBlockandaddsittothechain
- pass- v* }, M$ {9 v2 X) v3 I8 Z/ [& N
- defnew_transaction(self):
- #Addsanewtransactiontothelistoftransactions
- pass' {. f# A* l7 x% C; ?' ^
- @staticmethod- Z1 K* \7 _7 F! Y4 c
- defhash(block):
- #HashesaBlock) {2 D$ k- J0 L- I' v! X4 b0 g5 ] G( ?
- pass& b4 ?. v- A: h5 W, Z
- @property* `" }, d1 [7 z0 ?4 ?3 X
- deflast_block(self):3 z" P0 t. w1 |) N' i# b% b
- #ReturnsthelastBlockinthechain
- pass
-我们的区块链类的蓝图-
Blockchain类用来管理链条,它能存储交易,加入新块等,下面我们来进一步完善这些方法。
块结构
每个区块包含属性:索引(index),Unix时间戳(timestamp),交易列表(transactions),工作量证明(稍后解释)以及前一个区块的Hash值。- R$ Q. p. K2 g3 Y
以下是一个区块结构:1 L8 a, c; c- G4 _( H
- block={; a1 e' c% [* F4 M
- 'index':1,
- 'timestamp':1506057125.900785,
- 'transactions':[
- {
- 'sender':"8527147fe1f5426f9dd545de4b27ee00",
- 'recipient':"a77f5cdfa2934df3954a5c7c7da5df1f",
- 'amount':5,
- }7 s1 ^& k' z8 j, C, c
- ],
- 'proof':324984774000,
- 'previous_hash':"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", ~) L6 y* N' w0 e9 S
- }
-链上一个区块的例子-: ]& T' z+ D3 [' x, [& c4 Z$ i
到这里,区块链的概念就清楚了,每个新的区块都包含上一个区块的Hash,这是关键的一点,它保障了区块链不可变性。如果攻击者破坏了前面的某个区块,那么后面所有区块的Hash都会变得不正确。( Z1 |& C1 v, Q: f
确定这有用吗?嗯,你可以花点彻底了解它——这可是区块链背后的核心理念。
加入交易- F! l1 ^* Z5 e' g2 V; f
接下来我们需要添加一个交易,来完善下new_transaction()方法。1 y9 O" i/ k) H9 ~5 w
- classBlockchain(object):
- ...
- defnew_transaction(self,sender,recipient,amount):
- """& h! m- F1 |7 j& y
- 生成新交易信息,信息将加入到下一个待挖的区块中' q. m3 ^# q$ y) J q
- :paramsender:AddressoftheSender
- :paramrecipient:AddressoftheRecipient
- :paramamount:Amount
- :return:TheindexoftheBlockthatwillholdthistransaction/ y6 B4 F6 k; a) _7 l/ l9 _
- """
- self.current_transactions.append({4 x) e" N" A- ]! h$ ?
- 'sender':sender,
- 'recipient':recipient,2 Q2 d( m" e- q0 K
- 'amount':amount,7 c7 R7 a8 J) o/ v7 q: E$ D, r; c
- }), \; v: ^9 u1 A3 V% y0 t8 F
- returnself.last_block['index']+1
new_transaction()方法向列表中添加一个交易记录,并返回该记录将被添加到的区块(下一个待挖掘的区块)的索引,等下在用户提交交易时会有用。6 V2 p* r2 Y$ o) ?# b
创建区块
当Blockchain实例化后,我们需要构造一个创世块(没有前区块的第一个区块),并且给它加上一个“工作量证明”。
每个区块都需要经过工作量证明,俗称挖矿,稍后会继续讲解。1 b5 ?" S' v# X3 r( Q5 J- u- c
为了构造创世块,我们还需要完善new_block()、new_transaction()和hash()方法:
- importhashlib
- importjson. h3 ^* b: M& j2 ?
- fromtimeimporttime! S% E3 r# _+ n" @8 {3 P; {
- classBlockchain(object):' v: j9 f5 g+ H; z' j6 X
- def__init__(self):
- self.current_transactions=[]- n5 S* [% B( _. c9 s7 f. w
- self.chain=[]. p+ H2 \6 z1 y) z/ A4 [
- #Createthegenesisblock! F- D2 ~+ F+ k. L+ }
- self.new_block(previous_hash=1,proof=100)) P& @& Y6 {0 r) I
- defnew_block(self,proof,previous_hash=None):
- """
- 生成新块# l( ]& e. O v/ i& {; _
- :paramproof:TheproofgivenbytheProofofWorkalgorithm
- :paramprevious_hash:(Optional)HashofpreviousBlock
- :return:NewBlock' N0 r9 V; u% d% c. n% C
- """
- block={
- 'index':len(self.chain)+1,* U5 y( P0 @3 n
- 'timestamp':time(),: |5 {/ W6 q3 {. M4 e% O3 G
- 'transactions':self.current_transactions,
- 'proof':proof, k4 E$ q# E) ]" `" P% b" N6 ]* V; u
- 'previous_hash':previous_hashorself.hash(self.chain[-1]),
- }
- #Resetthecurrentlistoftransactions* M# U, e3 w, K. |$ H( e
- self.current_transactions=[]
- self.chain.append(block). _8 g( G, Y& e7 n
- returnblock
- defnew_transaction(self,sender,recipient,amount):) }; p8 k( e W
- """6 X& R. n H$ ]: X
- 生成新的交易信息,信息将加入到下一个待挖的区块中" x; J9 q( d1 O
- :paramsender:AddressoftheSender
- :paramrecipient:AddressoftheRecipient
- :paramamount:Amount
- :return:TheindexoftheBlockthatwillholdthistransaction& ^# @$ [$ Q. p6 ^
- """
- self.current_transactions.append({3 `; [1 D1 o& u+ Z
- 'sender':sender,
- 'recipient':recipient, |1 {5 T! l0 F* N# ^
- 'amount':amount,
- })
- returnself.last_block['index']+1
- @property
- deflast_block(self):
- returnself.chain[-1]
- @staticmethod8 D: {5 T9 ?! F( e4 j, p
- defhash(block):5 x' _2 G" m- s1 ^ e4 H
- """
- 生成块的SHA-256hash值
- :paramblock:Block
- :return:& P) K! @1 s! ~: w, n/ Z
- """
#WemustmakesurethattheDictionaryisOrdered,orwe'llhaveinconsistenthashes
block_string=json.dumps(block,sort_keys=True).encode()% J I7 o1 f$ z- K1 A; s
returnhashlib.sha256(block_string).hexdigest()9 z$ u2 b7 q& u4 F6 o1 |
上述应该是非常直接的——我已经添加了一些注释和字符来帮助大家理解。当表达出我们的区块链时,我们也快要完成了。但现在,你肯定在疑惑新区块是如何被创建、伪造或是挖出的。# c* \% C. e! f" t/ E
理解工作量证明
新的区块依赖工作量证明算法(PoW)来构造。PoW的目标是找出一个符合特定条件的数字,这个数字很难计算出来,但容易验证。这就是工作量证明的核心思想。
为了方便理解,我们举个例子:
假设一个整数x乘以另一个整数y的积的Hash值必须以0结尾,即hash(x*y)=ac23dc…0。设变量x=5,求y的值?用Python实现如下:
- fromhashlibimportsha256" i- n5 K: k/ O
- x=5% w9 q q/ C! M, T
- y=0#y未知8 |1 l; x0 ~- D
- whilesha256(f'{x*y}'.encode()).hexdigest()[-1]!="0":: P6 S5 ?: L" q$ W) V
- y+=19 J, X& T/ R6 O) D
- print(f'Thesolutionisy={y}')
结果y=21,因为:
hash(5*21)=1253e9373e...5e3600155e860
在比特币中,使用称为Hashcash的工作量证明算法,它和上面的问题很类似。矿工们为了争夺创建区块的权利而争相计算结果。通常,计算难度与目标字符串需要满足的特定字符的数量成正比,矿工算出结果后,会获得比特币奖励。
当然,在网络上非常容易验证这个结果。$ R0 ~7 l% y& v8 Y4 I( N
实现工作量证明% g2 r0 `( ]+ U( S5 H
让我们来实现一个相似PoW算法,规则是:
寻找一个数p,使得它与前一个区块的proof拼接成的字符串的Hash值以4个零开头。, S& C# p* }9 N3 t; x
- importhashlib
- importjson
- fromtimeimporttime
- fromuuidimportuuid48 h1 A- g- K( Y% O! }
- classBlockchain(object):
- ...8 e0 J( G% @6 Y$ }$ L& k% n
- defproof_of_work(self,last_proof):, n+ N+ E& M5 E& z6 A
- """
- 简单的工作量证明:
- -查找一个p'使得hash(pp')以4个0开头
- -p是上一个块的证明,p'是当前的证明
- :paramlast_proof:3 V! I0 B/ F' k ?5 q' \! x1 f
- :return:
- """
- proof=0
- whileself.valid_proof(last_proof,proof)isFalse:
- proof+=1
- returnproof
- @staticmethod" G# q: t1 i; g' l
- defvalid_proof(last_proof,proof):
- """& w, x3 Z0 F6 Y, ~( L
- 验证证明:是否hash(last_proof,proof)以4个0开头?
- :paramlast_proof:PreviousProof4 E- L) Q0 ]2 @* n
- :paramproof:CurrentProof% t: s+ @ R9 f* M y: s
- :return:Trueifcorrect,Falseifnot.. [" X; g) Z9 P: J) v
- """4 i% @$ v8 r2 E' Z4 o7 e# D
- guess=f'{last_proof}{proof}'.encode(): _7 q, n E6 ]# a0 [5 e
- guess_hash=hashlib.sha256(guess).hexdigest()0 n- Y" { d5 ~! Q
- returnguess_hash[:4]=="0000"" K( x5 o1 V- Q$ E! g, V
- ```
衡量算法复杂度的办法是修改零开头的个数。使用4个来用于演示,你会发现多一个零都会大大增加计算出结果所需的时间。
现在Blockchain类基本已经完成了,接下来使用HTTPrequests来进行交互。
##二、BlockChain作为API接口7 ]. T* L' s r Q+ H
我们将使用PythonFlask框架,这是一个轻量Web应用框架,它方便将网络请求映射到Python函数,现在我们来让Blockchain运行在基于Flaskweb上。
我们将创建三个接口:
- ```/transactions/new```创建一个交易并添加到区块) i0 \6 ^/ w) a% ]: H" O! x
- ```/mine```告诉服务器去挖掘新的区块
- ```/chain```返回整个区块链+ f* M. @+ A+ \1 U, z
- ###创建节点
- 我们的Flask服务器将扮演区块链网络中的一个节点。我们先添加一些框架代码:
- importhashlib
- importjson
- fromtextwrapimportdedent
- fromtimeimporttime+ w( k2 q5 v3 R0 Q
- fromuuidimportuuid4
- fromflaskimportFlask( y2 \8 D, O) X$ x/ ^' m# u
- classBlockchain(object):4 b) V$ H3 K0 h" ]; w, S
- …
- InstantiateourNode4 e1 [+ z) J+ p y# Y
- app=Flask(name)
- Generateagloballyuniqueaddressforthisnode
- node_identifier=str(uuid4()).replace(’-’,‘’)/ r/ _4 z, j# n, T5 }" t$ S6 C9 R
- InstantiatetheBlockchain2 c5 |# r' u0 E, c) @
- blockchain=Blockchain() W8 s0 Z. A2 j3 J% B
- @app.route(’/mine’,methods=[‘GET’]). u7 q5 a* B8 x0 Q8 t4 W4 P
- defmine():
- return“We’llmineanewBlock”2 w$ W, Y) I' N; _
- @app.route(’/transactions/new’,methods=[‘POST’])1 k# }5 z/ p( \3 m
- defnew_transaction():0 z- V, g% G, `" c; n
- return“We’lladdanewtransaction”
- @app.route(’/chain’,methods=[‘GET’])
- deffull_chain():
- response={& F, q% ?9 C5 D; W0 ?- W3 I
- ‘chain’:blockchain.chain,# P- U9 i5 m, `6 v9 w( c- M
- ‘length’:len(blockchain.chain)," n) F6 i# S7 `) x. |/ h
- }
- returnjsonify(response),200+ N1 ~ [$ l: } b/ G P
- ifname==‘main’:
- app.run(host=‘0.0.0.0’,port=5000)
- ```4 V/ c4 t2 |$ `# t" |' V
- 简单的说明一下以上代码:3 ~" ^: G+ z t& }7 E
- 第15行:创建一个节点。在这里阅读更多关于Flask的东西。
- 第18行:为节点创建一个随机的名字。/ _4 }( V: W+ x( @( ^
- 第21行:实例Blockchain类。& \1 ~2 m# {! |1 S
- 第24–26行:创建/mineGET接口。6 L) _) U }$ D
- 第28–30行:创建/transactions/newPOST接口,可以给接口发送交易数据。
- 第32–38行:创建/chain接口,返回整个区块链。4 }) H3 S6 w1 C/ {1 f8 X
- 第40–41行:服务运行在端口5000上。
- 发送交易
- 发送到节点的交易数据结构如下:
- {
- "sender":"myaddress",
- "recipient":"someoneelse'saddress",# ~- x6 M* ]7 t0 C% k9 ?
- "amount":5* @8 {0 p+ \. l/ N1 J( I
- }5 E- }6 Y4 {0 P* A% w# A4 h
- 之前已经有添加交易的方法,基于接口来添加交易就很简单了:
- importhashlib6 `, `7 V; s# f6 ~! j% T2 J4 R
- importjson
- fromtextwrapimportdedent
- fromtimeimporttime
- fromuuidimportuuid41 q( b+ K! H& i! _4 A6 a
- fromflaskimportFlask,jsonify,request
- ...
- @app.route('/transactions/new',methods=['POST'])
- defnew_transaction():
- values=request.get_json()0 G/ r" V( }8 X( i( Z
- #CheckthattherequiredfieldsareinthePOST'eddata: J C! W @+ {1 j; X; C0 F
- required=['sender','recipient','amount']
- ifnotall(kinvaluesforkinrequired):
- return'Missingvalues',400
- #CreateanewTransaction- J7 R. E( y F9 k
- index=blockchain.new_transaction(values['sender'],values['recipient'],values['amount'])
- response={'message':f'TransactionwillbeaddedtoBlock{index}'}: e9 G$ F9 ^$ ~1 b! Z3 ?
- returnjsonify(response),201
- ```
- -创建交易的方法-9 W: Z% U. B% u) }$ Z6 z
- ###挖矿* `: `( _. O$ x2 K [
- 挖矿正是神奇所在,它很简单,做了一下三件事:: ^$ {" ~9 X# e7 ~; Y& g9 r8 o
- 计算工作量证明PoW& _& v; y# D4 C6 g
- 通过新增一个交易授予矿工(自己)一个币
- 构造新区块并将其添加到链中8 \9 ^+ i* y2 B( u% F: E
- importhashlib' p3 x2 r4 g" O0 g8 z7 {
- importjson7 k: ?6 p: _6 X- j
- fromtimeimporttime& n9 b" D7 P% }3 T/ I: z% G3 z7 M
- fromuuidimportuuid4
- fromflaskimportFlask,jsonify,request
- …/ D; @5 T% P: Y$ E4 Q/ |/ {, ~
- @app.route(’/mine’,methods=[‘GET’])
- defmine():
- #Weruntheproofofworkalgorithmtogetthenextproof…+ {% K) l4 T0 J5 n1 n* |
- last_block=blockchain.last_block6 U8 N' z8 X$ ^( h' L5 z) A
- last_proof=last_block[‘proof’]$ y/ ]1 \$ \- T! \
- proof=blockchain.proof_of_work(last_proof)
- #给工作量证明的节点提供奖励.
- #发送者为"0"表明是新挖出的币.+ l5 V, l: }/ Q/ h! k. E7 y
- blockchain.new_transaction(
- sender="0",
- recipient=node_identifier,9 i( ~ _: L8 w* Q
- amount=1,& `' l' P i9 C' I9 |
- )
- #ForgethenewBlockbyaddingittothechain
- previous_hash=blockchain.hash(last_block)3 [6 `5 S0 a+ G: E6 w$ A
- block=blockchain.new_block(proof,previous_hash)
- response={! s7 m& O: G2 ~
- 'message':"NewBlockForged",
- 'index':block['index'],
- 'transactions':block['transactions'],* p, A; L) I% ^& I
- 'proof':block['proof'],
- 'previous_hash':block['previous_hash'],) d, x; }6 z+ z
- }
- returnjsonify(response),200
- ```
注意交易的接收者是我们自己的服务器节点,我们做的大部分工作都只是围绕Blockchain类方法进行交互。到此,我们的区块链就算完成了,我们来实际运行下。
三、运行区块链: x; g" ]9 r( d
你可以使用cURL或Postman去和API进行交互
启动server:
$pythonblockchain.py
*Runningonhttp://127.0.0.1:5000/(PressCTRL+Ctoquit)
让我们通过发送GET请求到http://localhost:5000/mine来进行挖矿:
-使用Postman以创建一个GET请求-$ ^& y% _/ p8 e2 Q
通过发送POST请求到http://localhost:5000/transactions/new,添加一个新交易:' T. j+ I+ H. B" p
-使用Postman以创建一个POST请求-$ n& Y2 p: L# r, ~ ~
如果不是使用Postman,则用一下的cURL语句也是一样的:; w7 `/ I$ z( p3 {! z
$curl-XPOST-H"Content-Type:application/json"-d'{
"sender":"d4ee26eee15148ee92c6cd394edd974e",4 E3 H" X& j: [" W
"recipient":"someone-other-address",
"amount":5: ?; ~# ^0 o+ I, c) b0 b
}'"http://localhost:5000/transactions/new"/ a5 o( G% I3 t) s
在挖了两次矿之后,就有3个块了,通过请求http://localhost:5000/chain可以得到所有的块信息:
- {
- "chain":[/ z* S; N) W5 M% U
- {
- "index":1,
- "previous_hash":1,
- "proof":100,# Y* m6 a F" V4 c) G l! r
- "timestamp":1506280650.770839,! c3 z* ]! F; U! @/ ^
- "transactions":[]
- },- a: x2 \7 P2 e( P) J/ q8 @
- {5 y6 C4 ]! h) X% A% ^3 m% a( Z
- "index":2,
- "previous_hash":"c099bc...bfb7",
- "proof":35293,
- "timestamp":1506280664.717925,
- "transactions":[% U& l$ E* }* F4 V1 k# a
- {
- "amount":1,
- "recipient":"8bbcb347e0634905b0cac7955bae152b",$ V9 H% T2 V, ~( E: J2 \5 w+ j# y6 i
- "sender":"0"
- }; A1 F: K" u: R7 F
- ]
- },: m5 o; K- R/ o' C! J( k# C
- {+ \& X( Z2 Y; p/ y) ?; ~; q
- "index":3,
- "previous_hash":"eff91a...10f2",3 B1 L& k/ O7 k" ^. w
- "proof":35089,/ O6 _1 Q& t2 q, h# `
- "timestamp":1506280666.1086972,
- "transactions":[& ]7 |/ O5 C5 c, \
- {
- "amount":1,
- "recipient":"8bbcb347e0634905b0cac7955bae152b",
- "sender":"0"
- }$ j4 v# v, W6 z" z: H5 r$ ?+ ]
- ]
- }
- ],
- "length":3, v/ P+ n/ _" O6 f, V! v
- }
四、一致性(共识)
非常棒,我们已经有了一个基本的区块链可以接受交易和挖矿。但是区块链系统应该是分布式的。既然是分布式的,那么我们究竟拿什么保证所有节点有同样的链呢?这就是一致性问题,我们要想在网络上有多个节点,就必须实现一个一致性的算法。
注册节点
在实现一致性算法之前,我们需要找到一种方式让一个节点知道它相邻的节点。每个节点都需要保存一份包含网络中其它节点的记录。因此让我们新增几个接口:. ?6 _. |0 K: I- ~3 ~7 K# A" e
/nodes/register接收URL形式的新节点列表% @4 C6 h1 B0 ?& L, l4 j/ r
/nodes/resolve执行一致性算法,解决任何冲突,确保节点拥有正确的链
我们修改下Blockchain的init函数并提供一个注册节点方法:
- ...5 i; F$ O$ N( s( O
- fromurllib.parseimporturlparse* V1 U% c) f0 M$ C2 F# U% I
- ...0 a9 i5 t$ O, ?% O
- classBlockchain(object):& ^: u3 K7 }. F* {: o5 k: T2 d( Y; e( A
- def__init__(self):
- ...
- self.nodes=set()8 h v8 u& b1 s t: r
- ...4 V* ?% L! |2 k G- K2 e% d5 k$ h
- defregister_node(self,address):/ N- O3 j5 O; i- \! O
- """
- Addanewnodetothelistofnodes
- :paramaddress:Addressofnode.Eg.'http://192.168.0.5:5000'- a# g% G" J1 l! c4 Y; f' X* S
- :return:None: y" L* x' ~- Q) v& r. U$ e" k1 X
- """
- parsed_url=urlparse(address)2 m4 X' C x% m1 h
- self.nodes.add(parsed_url.netloc)+ _) N u& d. E$ J7 {
- ```/ _' @5 J* w+ J
- 我们用set()来储存节点,这是一种避免重复添加节点的简单方法——这意味着无论我们添加特定节点多少次,它实际上都只添加了一次。
- ###实现共识算法3 C& F: d. o0 k/ M
- 前面提到,冲突是指不同的节点拥有不同的链,为了解决这个问题,规定最长的、有效的链才是最终的链,换句话说,网络中有效最长链才是实际的链。我们使用以下的算法,来达到网络中的共识。
- …
- importrequests
- classBlockchain(object)6 G0 B! \; S$ o* i e
- …5 {. t% F/ ]. t' I) l9 k
- defvalid_chain(self,chain):* I! M$ U8 J) Y! H
- """
- Determineifagivenblockchainisvalid
- :paramchain:Ablockchain! N" N" D. W) ~0 @) u! B
- :return:Trueifvalid,Falseifnot; R. O% f- j. |, I
- """
- last_block=chain[0]9 V$ `7 k' M1 `6 n" q) \
- current_index=17 i0 {' ?9 G- Z
- whilecurrent_indexmax_lengthandself.valid_chain(chain):2 g! S7 _, _6 C" i5 N
- max_length=length o8 F) p6 o- H+ d: A
- new_chain=chain
- #Replaceourchainifwediscoveredanew,validchainlongerthanours5 @; g* T" A! y: U* s" n
- ifnew_chain:$ x- h% [* M, ~# B
- self.chain=new_chain. z! I! n4 A+ p5 }6 l
- returnTrue
- returnFalse
- ```
第1个方法valid_chain()用来检查是否是有效链,遍历每个块验证hash和proof。
第2个方法resolve_conflicts()用来解决冲突,遍历所有的邻居节点,并用上一个方法检查链的有效性,如果发现有效更长链,就替换掉自己的链。: J5 u4 k3 U4 J; E
让我们添加两个路由,一个用来注册节点,一个用来解决冲突。( W0 Z; Q4 a+ o6 n
- @app.route('/nodes/register',methods=['POST'])
- defregister_nodes():& _ x) T! e3 l
- values=request.get_json()
- nodes=values.get('nodes')
- ifnodesisNone:5 j+ q' R5 R9 y2 e; d$ t$ v
- return"Error:Pleasesupplyavalidlistofnodes",400
- fornodeinnodes:* S7 e) s! E6 r
- blockchain.register_node(node) G$ R8 x0 B: b g2 U0 @ @
- response={" w$ \+ z& W1 V+ S, Q1 S6 C3 j
- 'message':'Newnodeshavebeenadded',
- 'total_nodes':list(blockchain.nodes),
- }0 L% c i% Z) {1 T! z
- returnjsonify(response),201
- @app.route('/nodes/resolve',methods=['GET'])8 y: h7 B$ l- V$ r" Q
- defconsensus():
- replaced=blockchain.resolve_conflicts()
- ifreplaced:5 x$ G( d- I o
- response={
- 'message':'Ourchainwasreplaced',
- 'new_chain':blockchain.chain
- }
- else:
- response={5 _& `, G* q" o. ~# k% }+ p
- 'message':'Ourchainisauthoritative',, Z; A. |0 S! n& R
- 'chain':blockchain.chain
- }% O7 j+ k8 |0 Q9 O) J4 c
- returnjsonify(response),200% ]2 j: e+ s: N9 P6 L" Z$ L
- ```
你可以在不同的机器运行节点,或在一台机机开启不同的网络端口来模拟多节点的网络,这里在同一台机器开启不同的端口演示,在不同的终端运行一下命令,就启动了两个节点:```http://localhost:5000```和```http://localhost:5001```。4 w+ K, z$ l, M8 |9 P& {3 j6 l
-注册一个新的节点-
然后在节点2上挖两个块,确保是更长的链,然后在节点1上访问GET/nodes/resolve,这时节点1的链会通过共识算法被节点2的链取代。