国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 專題 > 區(qū)塊鏈 > 正文

用Python實(shí)現(xiàn)一個(gè)區(qū)塊鏈

2020-02-25 19:29:02
字體:
供稿:網(wǎng)友

  相信你和我一樣對數(shù)字貨幣的崛起感到新奇,并且想知道其背后的技術(shù)——區(qū)塊鏈?zhǔn)窃鯓訉?shí)現(xiàn)的。

  但是理解區(qū)塊鏈并非易事,至少對于我來說是如此。晦澀難懂的視頻、漏洞百出的教程以及示例的匱乏令我倍受挫折。

  我喜歡在實(shí)踐中學(xué)習(xí),通過寫代碼來學(xué)習(xí)技術(shù)會掌握得更牢固。如果你也這樣做,那么讀完本文,你將獲得一個(gè)可用的區(qū)塊鏈以及對區(qū)塊鏈的深刻理解。

  開始之前...

  首先你需要知道區(qū)塊鏈?zhǔn)怯杀环Q為區(qū)塊的記錄構(gòu)成的不可變的、有序的鏈?zhǔn)浇Y(jié)構(gòu),這些記錄可以是交易、文件或任何你想要的數(shù)據(jù),最重要的是它們是通過 Hash 連接起來的。

  如果你不了解 Hash,這里有個(gè)例子

  其次,你需要安裝 Python3.6+,F(xiàn)lask,Request

  pip installFlask==0.12.2requests==2.18.4

  同時(shí)你還需要一個(gè) HTTP 客戶端,比如 Postman,cURL 或任何其它客戶端。

  最終的源代碼在這里:

  第一步: 打造一個(gè) Blockchain

  新建一個(gè)文件 blockchain.py,本文所有的代碼都寫在這一個(gè)文件中。首先創(chuàng)建一個(gè) Blockchain 類,在構(gòu)造函數(shù)中我們創(chuàng)建了兩個(gè)列表,一個(gè)用于儲存區(qū)塊鏈,一個(gè)用于儲存交易。

  classBlockchain(object):

  def__init__(self):

  self.chain=[]

  self.current_transactions=[]

  defnew_block(self):

  # Creates a new Block and adds it to the chain

  pass

  defnew_transaction(self):

  # Adds a new transaction to the list of transactions

  pass

  @staticmethod

  defhash(block):

  # Hashes a Block

  pass

  @property

  deflast_block(self):

  # Returns the last Block in the chain

  pass

  一個(gè)區(qū)塊有五個(gè)基本屬性:index,timestamp(in Unix time),transaction 列表,工作量證明(稍后解釋)以及前一個(gè)區(qū)塊的 Hash 值。

  block={

  'index':1,

  'timestamp':1506057125.900785,

  'transactions':[

  {

  'sender':"8527147fe1f5426f9dd545de4b27ee00",

  'recipient':"a77f5cdfa2934df3954a5c7c7da5df1f",

  'amount':5,

  }

  ],

  'proof':324984774000,

  'previous_hash':"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"

  }

  到這里,區(qū)塊鏈的概念應(yīng)該比較清楚了:每個(gè)新的區(qū)塊都會包含上一個(gè)區(qū)塊的 Hash 值。這一點(diǎn)非常關(guān)鍵,它是區(qū)塊鏈不可變性的根本保障。如果攻擊者破壞了前面的某個(gè)區(qū)塊,那么后面所有區(qū)塊的 Hash 都會變得不正確。不理解?慢慢消化~

  我們需要一個(gè)向區(qū)塊添加交易的方法:

  classBlockchain(object):

  ...

  defnew_transaction(self,sender,recipient,amount):

  """

  Creates a new transaction to go into the next mined Block

  :param sender: Address of the Sender

  :param recipient: Address of the Recipient

  :param amount: Amount

  :return: The index of the Block that will hold this transaction

  """

  self.current_transactions.append({

  'sender':sender,

  'recipient':recipient,

  'amount':amount,

  })

  returnself.last_block['index']+1

  new_transaction() 方法向列表中添加一個(gè)交易記錄,并返回該記錄將被添加到的區(qū)塊——下一個(gè)待挖掘的區(qū)塊——的索引,稍后在用戶提交交易時(shí)會有用。

  當(dāng) Blockchain 實(shí)例化后,我們需要?jiǎng)?chuàng)建一個(gè)初始的區(qū)塊(創(chuàng)世塊),并且給它預(yù)設(shè)一個(gè)工作量證明。

  除了添加創(chuàng)世塊的代碼,我們還需要補(bǔ)充 newblock(), newtransaction() 和 hash() 方法:

  importhashlib

  importjson

  fromtimeimporttime

  classBlockchain(object):

  def__init__(self):

  self.current_transactions=[]

  self.chain=[]

  # Create the genesis block

  self.new_block(previous_hash=1,proof=100)

  defnew_block(self,proof,previous_hash=None):

  block={

  'index':len(self.chain)+1,

  'timestamp':time(),

  'transactions':self.current_transactions,

  'proof':proof,

  'previous_hash':previous_hashorself.hash(self.chain[-1]),

  }

  # Reset the current list of transactions

  self.current_transactions=[]

  self.chain.append(block)

  returnblock

  defnew_transaction(self,sender,recipient,amount):

  self.current_transactions.append({

  'sender':sender,

  'recipient':recipient,

  'amount':amount,

  })

  returnself.last_block['index']+1

  @property

  deflast_block(self):

  returnself.chain[-1]

  @staticmethod

  defhash(block):

  block_string=json.dumps(block,sort_keys=True).encode()

  returnhashlib.sha256(block_string).hexdigest()

  上面的代碼應(yīng)該很直觀,我們基本上有了區(qū)塊鏈的雛形。但此時(shí)你肯定很想知道一個(gè)區(qū)塊究竟是怎樣被創(chuàng)建或挖掘出來的。

  新的區(qū)塊來自工作量證明(PoW)算法。PoW 的目標(biāo)是計(jì)算出一個(gè)符合特定條件的數(shù)字,這個(gè)數(shù)字對于所有人而言必須在計(jì)算上非常困難,但易于驗(yàn)證。這就是工作量證明的核心思想。

  舉個(gè)例子:

  假設(shè)一個(gè)整數(shù) x 乘以另一個(gè)整數(shù) y 的積的 Hash 值必須以 0 結(jié)尾,即 hash(x * y) = ac23dc...0。設(shè) x = 5,求 y?

  fromhashlibimportsha256

  x=5

  y=0# We don't know what y should be yet...

  whilesha256(f'{x*y}'.encode()).hexdigest()[-1]!="0":

  y+=1

  print(f'The solution is y = {y}')

  結(jié)果是 y = 21 // hash(5 * 21) = 1253e9373e...5e3600155e860

  在比特幣中,工作量證明算法被稱為 Hashcash,它和上面的問題很相似,只不過計(jì)算難度非常大。這就是礦工們?yōu)榱藸帄Z創(chuàng)建區(qū)塊的權(quán)利而爭相計(jì)算的問題。通常,計(jì)算難度與目標(biāo)字符串需要滿足的特定字符的數(shù)量成正比,礦工算出結(jié)果后,就會獲得一定數(shù)量的比特幣獎(jiǎng)勵(lì)(通過交易)。

  網(wǎng)絡(luò)要驗(yàn)證結(jié)果,當(dāng)然非常容易。

  讓我們來實(shí)現(xiàn)一個(gè) PoW 算法,和上面的例子非常相似,規(guī)則是:尋找一個(gè)數(shù) p,使得它與前一個(gè)區(qū)塊的 proof 拼接成的字符串的 Hash 值以 4 個(gè)零開頭。

  importhashlib

  importjson

  fromtimeimporttime

  fromuuidimportuuid4

  classBlockchain(object):

  ...

  defproof_of_work(self,last_proof):

  proof=0

  whileself.valid_proof(last_proof,proof)isFalse:

  proof+=1

  returnproof

  @staticmethod

  defvalid_proof(last_proof,proof):

  guess=f'{last_proof}{proof}'.encode()

  guess_hash=hashlib.sha256(guess).hexdigest()

  returnguess_hash[:4]=="0000"

  衡量算法復(fù)雜度的辦法是修改零的個(gè)數(shù)。4 個(gè)零足夠用于演示了,你會發(fā)現(xiàn)哪怕多一個(gè)零都會大大增加計(jì)算出結(jié)果所需的時(shí)間。

  我們的 Blockchain 基本已經(jīng)完成了,接下來我們將使用 HTTP requests 來與之交互。

  第二步:作為 API 的 Blockchain

  我們將使用 Flask 框架,它十分輕量并且很容易將網(wǎng)絡(luò)請求映射到 Python 函數(shù)。

  我們將創(chuàng)建三個(gè)接口:

  /transactions/new創(chuàng)建一個(gè)交易并添加到區(qū)塊

  /mine告訴服務(wù)器去挖掘新的區(qū)塊

  /chain返回整個(gè)區(qū)塊鏈

  我們的服務(wù)器將扮演區(qū)塊鏈網(wǎng)絡(luò)中的一個(gè)節(jié)點(diǎn)。我們先添加一些常規(guī)代碼:

  importhashlib

  importjson

  fromtextwrapimportdedent

  fromtimeimporttime

  fromuuidimportuuid4

  fromflaskimportFlask,jsonify,request

  classBlockchain(object):

  ...

  # Instantiate our Node

  app=Flask(__name__)

  # Generate a globally unique address for this node

  node_identifier=str(uuid4()).replace('-','')

  # Instantiate the Blockchain

  blockchain=Blockchain()

  @app.route('/mine',methods=['GET'])

  defmine():

  return"We'll mine a new Block"

  @app.route('/transactions/new',methods=['POST'])

  defnew_transaction():

  return"We'll add a new transaction"

  @app.route('/chain',methods=['GET'])

  deffull_chain():

  response={

  'chain':blockchain.chain,

  'length':len(blockchain.chain),

  }

  returnjsonify(response),200

  if__name__=='__main__':

  app.run(host='127.0.0.1',port=5000)

  這是用戶發(fā)起交易時(shí)發(fā)送到服務(wù)器的請求:

  {

  "sender":"my address",

  "recipient":"someone else's address",

  "amount":5

  }

  我們已經(jīng)有了向區(qū)塊添加交易的方法,因此剩下的部分就很簡單了:

  @app.route('/transactions/new',methods=['POST'])

  defnew_transaction():

  values=request.get_json()

  # Check that the required fields are in the POST'ed data

  required=['sender','recipient','amount']

  ifnotall(kinvaluesforkinrequired):

  return'Missing values',400

  # Create a new Transaction

  index=blockchain.new_transaction(values['sender'],values['recipient'],values['amount'])

  response={'message':f'Transaction will be added to Block {index}'}

  returnjsonify(response),201

  挖掘端正是奇跡發(fā)生的地方,它只做三件事:計(jì)算 PoW;通過新增一個(gè)交易授予礦工一定數(shù)量的比特幣;構(gòu)造新的區(qū)塊并將其添加到區(qū)塊鏈中。

  @app.route('/mine',methods=['GET'])

  defmine():

  # We run the proof of work algorithm to get the next proof...

  last_block=blockchain.last_block

  last_proof=last_block['proof']

  proof=blockchain.proof_of_work(last_proof)

  # We must receive a reward for finding the proof.

  # The sender is "0" to signify that this node has mined a new coin.

  blockchain.new_transaction(

  sender="0",

  recipient=node_identifier,

  amount=1,

  )

  # Forge the new Block by adding it to the chain

  block=blockchain.new_block(proof)

  response={

  'message':"New Block Forged",

  'index':block['index'],

  'transactions':block['transactions'],

  'proof':block['proof'],

  'previous_hash':block['previous_hash'],

  }

  returnjsonify(response),200

  需注意交易的接收者是我們自己的服務(wù)器節(jié)點(diǎn),目前我們做的大部分事情都只是圍繞 Blockchain 類進(jìn)行交互。到此,我們的區(qū)塊鏈就算完成了。

  第三步:交互演示

  使用 Postman 演示,略。

  第四步:一致性

  這真的很棒,我們已經(jīng)有了一個(gè)基本的區(qū)塊鏈可以添加交易和挖礦。但是,整個(gè)區(qū)塊鏈系統(tǒng)必須是分布式的。既然是分布式的,那么我們究竟拿什么保證所有節(jié)點(diǎn)運(yùn)行在同一條鏈上呢?這就是一致性問題,我們要想在網(wǎng)絡(luò)中添加新的節(jié)點(diǎn),就必須實(shí)現(xiàn)保證一致性的算法。

  在實(shí)現(xiàn)一致性算法之前,我們需要找到一種方式讓一個(gè)節(jié)點(diǎn)知道它相鄰的節(jié)點(diǎn)。每個(gè)節(jié)點(diǎn)都需要保存一份包含網(wǎng)絡(luò)中其它節(jié)點(diǎn)的記錄。讓我們新增幾個(gè)接口:

  1./nodes/register接收以URL的形式表示的新節(jié)點(diǎn)的列表

  2./nodes/resolve用于執(zhí)行一致性算法,用于解決任何沖突,確保節(jié)點(diǎn)擁有正確的鏈

  ...

  fromurllib.parseimporturlparse

  ...

  classBlockchain(object):

  def__init__(self):

  ...

  self.nodes=set()

  ...

  defregister_node(self,address):

  parsed_url=urlparse(address)

  self.nodes.add(parsed_url.netloc)

  注意到我們用 set 來儲存節(jié)點(diǎn),這是一種避免重復(fù)添加節(jié)點(diǎn)的簡便方法。

  前面提到的沖突是指不同的節(jié)點(diǎn)擁有的鏈存在差異,要解決這個(gè)問題,我們規(guī)定最長的合規(guī)的鏈就是最有效的鏈,換句話說,只有最長且合規(guī)的鏈才是實(shí)際存在的鏈。

  讓我們再添加兩個(gè)方法,一個(gè)用于添加相鄰節(jié)點(diǎn),另一個(gè)用于解決沖突。

  ...

  importrequests

  classBlockchain(object)

  ...

  defvalid_chain(self,chain):

  last_block=chain[0]

  current_index=1

  whilecurrent_index(chain):

  block=chain[current_index]

  print(f'{last_block}')

  print(f'{block}')

  print("n-----------n")

  # Check that the hash of the block is correct

  ifblock['previous_hash']!=self.hash(last_block):

  returnFalse

  # Check that the Proof of Work is correct

  ifnotself.valid_proof(last_block['proof'],block['proof']):

  returnFalse

  last_block=block

  current_index+=1

  returnTrue

  defresolve_conflicts(self):

  neighbours=self.nodes

  new_chain=None

  # We're only looking for chains longer than ours

  max_length=len(self.chain)

  # Grab and verify the chains from all the nodes in our network

  fornodeinneighbours:

  response=requests.get(f'http://{node}/chain')

  ifresponse.status_code==200:

  length=response.json()['length']

  chain=response.json()['chain']

  # Check if the length is longer and the chain is valid

  iflength>max_lengthandself.valid_chain(chain):

  max_length=length

  new_chain=chain

  # Replace our chain if we discovered a new, valid chain longer than ours

  ifnew_chain:

  self.chain=new_chain

  returnTrue

  returnFalse

  現(xiàn)在你可以新開一臺機(jī)器,或者在本機(jī)上開啟不同的網(wǎng)絡(luò)接口來模擬多節(jié)點(diǎn)的網(wǎng)絡(luò),或者邀請一些朋友一起來測試你的區(qū)塊鏈。

  我希望本文能激勵(lì)你創(chuàng)造更多新東西。我之所以對數(shù)字貨幣入迷,是因?yàn)槲蚁嘈艆^(qū)塊鏈會很快改變我們看待事物的方式,包括經(jīng)濟(jì)、政府、檔案管理等。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 永兴县| 镇康县| 甘肃省| 乌鲁木齐市| 本溪| 汕头市| 浠水县| 定襄县| 娱乐| 昌都县| 库尔勒市| 东源县| 万州区| 平果县| 汉中市| 宁乡县| 洛浦县| 德州市| 进贤县| 新巴尔虎右旗| 枣阳市| 凉城县| 汉寿县| 新河县| 内江市| 洪洞县| 商都县| 曲阳县| 新兴县| 吉安市| 石首市| 恩施市| 方城县| 宜城市| 东丰县| 精河县| 临沂市| 泸州市| 江川县| 翁牛特旗| 内乡县|