久久ER99热精品一区二区-久久精品99国产精品日本-久久精品免费一区二区三区-久久综合九色综合欧美狠狠

博客專欄

EEPW首頁 > 博客 > Python視頻教程之用Django實現一個可運行的區塊鏈應用分享

Python視頻教程之用Django實現一個可運行的區塊鏈應用分享

發布人:扣丁學習 時間:2020-11-27 來源:工程師 發布文章

  對數字貨幣的崛起感到新奇的我們,并且想知道其背后的技術——區塊鏈是怎樣實現的。

  但是完全搞懂區塊鏈并非易事,我喜歡在實踐中學習,通過寫代碼來學習技術會掌握得更牢固。通過構建一個區塊鏈可以加深對區塊鏈的理解。下面內容對Python有基本的理解,能讀寫基本的Python,并且需要對HTTP請求有基本的了解。


  我們知道區塊鏈是由區塊的記錄構成的不可變、有序的鏈結構,記錄可以是交易、文件或任何你想要的數據,重要的是它們是通過哈希值(hashes)鏈接起來的。


  環境準備環境準備,確保已經安裝Python3.5,pip,django,requests,urllib,json,hashlib安裝方法:


  pipinstalldjangorequests


  同時還需要一個HTTP客戶端,比如Postman,cURL或其它客戶端,本文以Postman為例。


  開始創建Blockchain通過django-adminstartprojectblock創建一個block的項目,在項目中創建一個demo項目django-adminstartprojectdemo,目錄結構:


  Blockchain類在views中創建一個Blockchain類,在構造函數中創建了兩個列表,一個用于儲存區塊鏈,一個用于儲存交易。


  以下是Blockchain類的框架:


  class


  Blockchain


  (


  object


  ):


  def


  __init__(


  self


  ):


  self


  .chain=[]


  self


  .current_transactions=[]


  def


  new_block(


  self


  ):


  #CreatesanewBlockandaddsittothechain


  pass


  def


  new_transaction(


  self


  ):


  #Addsanewtransactiontothelistoftransactions


  pass


  @staticmethod


  def


  hash(block):


  #HashesaBlock


  pass


  @property


  def


  last_block(


  self


  ):


  #ReturnsthelastBlockinthechain


  pass


  Blockchain類用來管理鏈條,它能存儲交易,加入新塊等,下面我們來進一步完善這些方法。


  塊結構每個區塊包含屬性:索引(index),Unix時間戳(timestamp),交易列表(transactions),工作量證明(稍后解釋)以及前一個區塊的Hash值。


  以下是一個區塊的結構:


  block={


  'index'


  :


  1


  ,


  'timestamp'


  :


  1506057125.900785


  ,


  'transactions'


  :[


  {


  'sender'


  :


  "8527147fe1f5426f9dd545de4b27ee00"


  ,


  'recipient'


  :


  "a77f5cdfa2934df3954a5c7c7da5df1f"


  ,


  'amount'


  :


  5


  ,


  }


  ],


  'proof'


  :


  324984774000


  ,


  'previous_hash'


  :


  "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"


  }


  到這里,區塊鏈的概念就清楚了,每個新的區塊都包含上一個區塊的Hash,這是關鍵的一點,它保障了區塊鏈不可變性。如果攻擊者破壞了前面的某個區塊,那么后面所有區塊的Hash都會變得不正確。不理解的話,慢慢消化。


  加入交易接下來我們需要添加一個交易,來完善下new_transaction方法


  class


  Blockchain


  (


  object


  ):


  ...


  def


  new_transaction(


  self


  ,sender,recipient,amount):


  """


  生成新交易信息,信息將加入到下一個待挖的區塊中


  :paramsender:<str>AddressoftheSender


  :paramrecipient:<str>AddressoftheRecipient


  :paramamount:<int>Amount


  :return:<int>TheindexoftheBlockthatwillholdthistransaction


  """


  self


  .current_transactions.append({


  'sender'


  :sender,


  'recipient'


  :recipient,


  'amount'


  :amount,


  })


  return


  self


  .last_block[


  'index'


  ]+


  1


  方法向列表中添加一個交易記錄,并返回該記錄將被添加到的區塊(下一個待挖掘的區塊)的索引,等下在用戶提交交易時會有用。


  創建新塊


  當Blockchain實例化后,我們需要構造一個創世塊(沒有前區塊的第一個區塊),并且給它加上一個工作量證明。每個區塊都需要經過工作量證明,俗稱挖礦,稍后會繼續講解。


  為了構造創世塊,我們還需要完善newblock(),newtransaction()和hash()方法:


  class


  Blockchain


  (


  object


  ):


  def


  __init__(


  self


  ):


  self


  .chain=[]


  self


  .current_transactions=[]


  self


  .new_block(previous_hash=


  1


  ,proof=


  100


  )


  self


  .nodes=


  set


  ()


  def


  new_block(


  self


  ,proof,previous_hash=


  None


  ):


  block={


  'index'


  :len(


  self


  .chain)+


  1


  ,


  'timestamp'


  :time(),


  'transactions'


  :


  self


  .current_transactions,


  'proof'


  :proof,


  'previous_hash'


  :previous_hash


  or


  self


  .hash(


  self


  .chain[-


  1


  ]),


  }


  self


  .current_transactions=[]


  self


  .chain.append(block)


  return


  block


  def


  new_transaction(


  self


  ,sender,recipient,amount):


  self


  .current_transactions.append({


  'sender'


  :sender,


  'recipient'


  :recipient,


  'amount'


  :amount,


  })


  return


  self


  .last_block[


  'index'


  ]+


  1


  @staticmethod


  def


  hash(block):


  block_string=json.dumps(block,sort_keys=


  True


  ).encode()


  return


  hashlib.sha256(block_string).hexdigest()


  通過上面的代碼和注釋可以對區塊鏈有直觀的了解,接下來我們看看區塊是怎么挖出來的。


  理解工作量證明新的區塊依賴工作量證明算法(PoW)來構造。PoW的目標是找出一個符合特定條件的數字,這個數字很難計算出來,但容易驗證。這就是工作量證明的核心思想。


  為了方便理解,舉個例子:


  假設一個整數x乘以另一個整數y的積的Hash值必須以0結尾,即hash(x*y)=ac23dc…0。設變量x=5,求y的值?


  用Python實現如下:


  from


  hashlib


  import


  sha256


  x=


  5


  y=


  0


  while


  sha256(str(x*y).encode()).hexdigest()[:


  4


  ]!=


  "0000"


  :


  y+=


  1


  print


  (y,sha256(str(x*y).encode()).hexdigest()[:


  4


  ])


  print


  (y)


  在比特幣中,使用稱為Hashcash的工作量證明算法,它和上面的問題很類似。礦工們為了爭奪創建區塊的權利而爭相計算結果。通常,計算難度與目標字符串需要滿足的特定字符的數量成正比,礦工算出結果后,會獲得比特幣獎勵。當然,在網絡上非常容易驗證這個結果。


  實現工作量證明讓我們來實現一個相似PoW算法,規則是:尋找一個數p,使得它與前一個區塊的proof拼接成的字符串的Hash值以4個零開頭。


  import


  hashlib


  import


  json


  from


  time


  import


  time


  from


  uuid


  import


  uuid4


  class


  Blockchain


  (


  object


  ):


  ...


  def


  last_block(


  self


  ):


  return


  self


  .chain[-


  1


  ]


  def


  proof_of_work(


  self


  ,last_proof):


  proof=


  0


  while


  self


  .valid_proof(last_proof,proof)


  is


  False


  :


  proof+=


  1


  return


  proof


  @staticmethod


  def


  valid_proof(last_proof,proof):


  guess=str(last_proof*proof).encode()


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


  return


  guess_hash[:


  5


  ]==


  "00000"


  衡量算法復雜度的辦法是修改零開頭的個數。使用4個來用于演示,你會發現多一個零都會大大增加計算出結果所需的時間。


  現在Blockchain類基本已經完成了,接下來使用HTTPrequests來進行交互。


  Blockchain作為API接口我們將使用Pythondjango框架,這是一個輕量Web應用框架,它方便將網絡請求映射到Python函數,現在我們來讓來試一下:


  我們將創建三個接口:


  /transactions/new創建一個交易并添加到區塊


  /mine告訴服務器去挖掘新的區塊


  /chain返回整個區塊鏈


  創建節點我們的“djangoweb服務器”將扮演區塊鏈網絡中的一個節點。我們先添加一些框架代碼:


  node_identifier=str(uuid4()).replace(


  '-'


  ,


  ''


  )


  #InstantiatetheBlockchain


  blockchain=


  Blockchain


  ()


  def


  mine(request):


  last_block=blockchain.last_block


  last_proof=last_block[


  'proof'


  ]


  proof=blockchain.proof_of_work(last_proof)


  print


  (proof)


  blockchain.new_transaction(


  sender=


  "0"


  ,


  recipient=node_identifier,


  amount=


  1


  ,


  )


  #ForgethenewBlockbyaddingittothechain


  block=blockchain.new_block(proof)


  response={


  'message'


  :


  "NewBlockForged"


  ,


  'index'


  :block[


  'index'


  ],


  'transactions'


  :block[


  'transactions'


  ],


  'proof'


  :block[


  'proof'


  ],


  'previous_hash'


  :block[


  'previous_hash'


  ],


  }


  print


  (response)


  return


  HttpResponse


  (json.dumps(response))


  def


  new_transaction(request):


  values=json.loads(request.body.decode(


  'utf-8'


  ))


  required=[


  'sender'


  ,


  'recipient'


  ,


  'amount'


  ]


  if


  not


  all(k


  in


  values


  for


  k


  in


  required):


  return


  'Missingvalues'


  index=blockchain.new_transaction(values[


  'sender'


  ],values[


  'recipient'


  ],values[


  'amount'


  ])


  print


  (index)


  response={


  'message'


  :


  'TransactionwillbeaddedtoBlock%s'


  %index}


  return


  HttpResponse


  (json.dumps(response))


  def


  full_chain(request):


  response={


  'chain'


  :blockchain.chain,


  'length'


  :len(blockchain.chain),


  }


  return


  HttpResponse


  (json.dumps(response))


  添加url路由節點:運行服務


  from


  demo


  import


  views


  urlpatterns=[


  url(r


  '^admin/'


  ,admin.site.urls),


  url(r


  '^mine'


  ,views.mine),


  url(r


  '^transactions/new/'


  ,views.new_transaction),


  url(r


  '^chain/'


  ,views.full_chain),


  url(r


  '^register'


  ,views.register_nodes),


  url(r


  '^resolve'


  ,views.consensus),


  ]


  運行服務


  pythonmanage.pyrunserver


  127.0


  .


  0.1


  :


  8000


  發送交易發送到節點的交易數據,結構如下:


  {


  "sender"


  :


  "myaddress"


  ,


  "recipient"


  :


  "someoneelse'saddress"


  ,


  "amount"


  :


  5


  }


  向服務添加一個交易


  挖礦挖礦正是神奇所在,它很簡單,做了一下三件事:


  1、計算工作量證明PoW


  2、通過新增一個交易授予礦工(自己)一個幣


  3、構造新區塊并將其添加到鏈中


  def


  proof_of_work(


  self


  ,last_proof):


  proof=


  0


  while


  self


  .valid_proof(last_proof,proof)


  is


  False


  :


  proof+=


  1


  return


  proof


  @staticmethod


  def


  valid_proof(last_proof,proof):


  guess=str(last_proof*proof).encode()


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


  return


  guess_hash[:


  5


  ]==


  "00000"


  注意交易的接收者是我們自己的服務器節點,我們做的大部分工作都只是圍繞Blockchain類方法進行交互。到此,我們的區塊鏈就算完成了,我們來實際運行下


  運行區塊鏈使用Postman去和API進行交互


  讓我們通過請求http://127.0.0.1:8000/mine來進行挖礦


  在挖了兩次礦之后,就有3個塊了,通過請求http://localhost:8000/chain可以得到所有的塊信息。


  {


  "chain"


  :[


  {


  "transactions"


  :[],


  "proof"


  :


  100


  ,


  "timestamp"


  :


  1520314374.7261052


  ,


  "index"


  :


  1


  ,


  "previous_hash"


  :


  1


  },


  {


  "transactions"


  :[


  {


  "sender"


  :


  "0"


  ,


  "recipient"


  :


  "27d4aae55b2848dcae52bc722d86e0c3"


  ,


  "amount"


  :


  1


  }


  ],


  "proof"


  :


  1771087


  ,


  "timestamp"


  :


  1520314389.5019505


  ,


  "index"


  :


  2


  ,


  "previous_hash"


  :


  "32fa73f48240160257e95fdf8422c6df734b5d7e8ceb69a41a5578643c1d36fb"


  },


  {


  "transactions"


  :[


  {


  "sender"


  :


  "d4ee26eee15148ee92c6cd394edd9705"


  ,


  "recipient"


  :


  "5"


  ,


  "amount"


  :


  500


  },


  {


  "sender"


  :


  "0"


  ,


  "recipient"


  :


  "27d4aae55b2848dcae52bc722d86e0c3"


  ,


  "amount"


  :


  1


  }


  ],


  "proof"


  :


  100


  ,


  "timestamp"


  :


  1520314592.4745598


  ,


  "index"


  :


  3


  ,


  "previous_hash"


  :


  "e6b1be488e0ed20fe3ec51135e5fafb4dfffaa28a190967106a5dd3e89e4b3aa"


  }


  ],


  "length"


  :


  3


  }


  一致性(共識)我們已經有了一個基本的區塊鏈可以接受交易和挖礦。但是區塊鏈系統應該是分布式的。既然是分布式的,那么我們究竟拿什么保證所有節點有同樣的鏈呢?這就是一致性問題,我們要想在網絡上有多個節點,就必須實現一個一致性的算法。


  注冊節點在實現一致性算法之前,我們需要找到一種方式讓一個節點知道它相鄰的節點。每個節點都需要保存一份包含網絡中其它節點的記錄。因此讓我們新增幾個接口:


  1、/register接收URL形式的新節點列表


  2、/resolve執行一致性算法,解決任何沖突,確保節點擁有正確的鏈我們修改下Blockchain的init函數并提供一個注冊節點方法:


  from


  urllib.parse


  import


  urlparse


  ...


  class


  Blockchain


  (


  object


  ):


  def


  __init__(


  self


  ):


  ...


  self


  .nodes=


  set


  ()


  ...


  def


  register_node(


  self


  ,address):


  parsed_url=urlparse(address)


  self


  .nodes.add(parsed_url.netloc)


  我們用set來儲存節點,這是一種避免重復添加節點的簡單方法。


  實現共識算法前面提到,沖突是指不同的節點擁有不同的鏈,為了解決這個問題,規定最長的、有效的鏈才是最終的鏈,換句話說,網絡中有效最長鏈才是實際的鏈。


  我們使用一下的算法,來達到網絡中的共識


  class


  Blockchain


  (


  object


  ):


  def


  __init__(


  self


  ):


  ...


  def


  valid_chain(


  self


  ,chain):


  last_block=chain[


  0


  ]


  current_index=


  1


  while


  current_index<len(chain):


  block=chain[current_index]


  if


  block[


  'previous_hash'


  ]!=


  self


  .hash(last_block):


  return


  False


  #CheckthattheProofofWorkiscorrect


  if


  not


  self


  .valid_proof(last_block[


  'proof'


  ],block[


  'proof'


  ]):


  return


  False


  last_block=block


  current_index+=


  1


  return


  True


  def


  resolve_conflicts(


  self


  ):


  neighbours=


  self


  .nodes


  new_chain=


  None


  max_length=len(


  self


  .chain)


  for


  node


  in


  neighbours:


  response=requests.


  get


  (


  'http://%s/chain'


  %node)


  if


  response.status_code==


  200


  :


  length=json.loads(response)[


  'length'


  ]


  chain=json.loads(response)[


  'chain'


  ]


  #Checkifthelengthislongerandthechainisvalid


  if


  length>max_length


  and


  self


  .valid_chain(chain):


  max_length=length


  new_chain=chain


  #Replaceourchainifwediscoveredanew,validchainlongerthanours


  if


  new_chain:


  self


  .chain=new_chain


  return


  True


  return


  False


  第一個方法valid_chain()用來檢查是否是有效鏈,遍歷每個塊驗證hash和proof.


  第2個方法resolve_conflicts()用來解決沖突,遍歷所有的鄰居節點,并用上一個方法檢查鏈的有效性,如果發現有效更長鏈,就替換掉自己的鏈


  在url中添加兩個路由,一個用來注冊節點,一個用來解決沖突。


  from


  demo


  import


  views


  urlpatterns=[


  url(r


  '^register'


  ,views.register_nodes),


  url(r


  '^resolve'


  ,views.consensus),


  ]


  你可以在不同的機器運行節點,或在一臺機機開啟不同的網絡端口來模擬多節點的網絡,這里在同一臺機器開啟不同的端口演示,在不同的終端運行一下命令,就啟動了兩個節點:http://127.0.0.1:8000和http://127.0.0.1:8100


  然后在節點8100節點上挖兩個塊,確保是更長的鏈,然后在節點8000節點上訪問接口/resolve,這時節點8100的鏈會通過共識算法被節點8000節點的鏈取代。


  最后想要了解更多關于Python發展前景趨勢,請關注扣丁學堂python培訓官網、微信等平臺,扣丁學堂IT職業在線學習教育平臺為您提供最新的Python視頻教程系統,通過千鋒扣丁學堂金牌講師在線錄制的Python視頻教程課程,讓你快速掌握Python從入門到精通開發實戰技能。扣丁學堂Python技術交流群:816572891。

*博客內容為網友個人發布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。



關鍵詞:

相關推薦

技術專區

關閉