通過寫一個區(qū)塊鏈來學習區(qū)塊鏈 101

區(qū)塊鏈齊步走

想要理解區(qū)塊鏈如何工作最好的方式就是制作一個啦沛申。

Learn Blockchains by Building One
加密貨幣與區(qū)塊鏈(三):什么是信任

Keywords

ruby, blockchain, consensus

A

作為一個匪菜大軍中的一員铁材,不了解區(qū)塊鏈是不能稱為一個合格的匪菜的奕锌≈酰空氣幣的火熱,讓更多的匪菜充滿了渴望惊暴,似乎我們也需要了解一些在這個背后的基礎(chǔ)饼丘。了解這個其實并不是很簡單,因為更多的匪菜喜歡看到的是綠色的漲幅辽话,而不是背后的技術(shù)肄鸽,你可以在得(不)道上面找到很多奇怪的Talk,但是你的確得不到油啤。
我喜歡邊做邊學典徘,看完下面的例子,我相信你能成為一顆不一樣的匪菜逮诲。

B

區(qū)塊鏈(Blockchain),顧名思義就是由塊組成的鏈,每一個塊就是一些數(shù)據(jù)梅鹦,然后通過一種手法把這個塊連接起來裆甩,就是用一個哈希函數(shù) H,把block B[i]的哈希值 H(B[i]) 包含在下一個 block B[i+1] 里齐唆。H 具有單向性淑掌,也就是知道 B 就很容易算出 H(B),但是反過來如果只知道 H(B) 的值很難構(gòu)造出一個滿足條件的 B蝶念。當然啦,這個其實就是一個鏈表芋绸,POC媒殉。這樣做的結(jié)果就意味著如果其中任何一塊被修改了。而因為 H(B0) 是 B1 的一部分摔敛,所以導致 H(B1) 也要跟著變廷蓉。如果有人要修改記錄在這個鏈上的數(shù)據(jù),就需要修改后面所有的塊马昙。這個就叫做Merkle List桃犬。如果你用過Gayhub,那么你應該也知道行楞,Git存儲的方式就是基于Merkle List攒暇。

C

在你開始之前,我和你們說這篇教程使用的是Ruby語言寫的子房。這里用了一些很簡單的庫來幫助我們可以做一個簡單的Web Application形用,cuba, faraday。這里就不多做解釋了证杭。

STEP 1

在開始前田度,你可以在這里看到源代碼傳送門

我們在這里創(chuàng)建一個Blockchain的Blueprint

class Blockchain
end

Emmmm, that was a joke.

class Blockchain

  class << self
    def hash blk
      
    end
  end
  def initialize
  end

  def new_block
  end

  def new_transacction
  end

  def last_block
  end
end

我們的Blockchain是用來對鏈初始化,然后添加一些常用的操作的解愤,new_block, new_transaction, hash等镇饺。

那么一個Block應該是什么樣子的呢?

block = {
    'index': 1,
    'timestamp': 1506057125,
    'transactions': [
        {
            'sender': "8527147fe1f5426f9dd545de4b27ee00",
            'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",
            'amount': 5,
        }
    ],
    'proof': 324984774000,
    'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
}

接下來我們需要創(chuàng)建新的塊了送讲,在我們的Blockchain初始化的時候奸笤,我們需要給他一個創(chuàng)世塊,一個沒有祖先的塊李茫,同時我們也需要給創(chuàng)世塊增加一個 proof 揭保,這是挖礦的結(jié)果,我稍后再說啦魄宏。

我們現(xiàn)在需要了解什么是 PoW (Proof of Work)秸侣,顧名思義就是新的區(qū)塊是如何產(chǎn)生或者如何被挖出來的,它存在的目的就是發(fā)現(xiàn)能夠解決一個問題的數(shù)字,這個數(shù)字需要具備兩個屬性味榛,難找和易驗證椭坚。

我們舉一個簡單的例子

from hashlib import sha256
x = 5
y = 0  # We don't know what y should be yet...
while sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0":
    y += 1
print(f'The solution is y = {y}')

那么結(jié)果就是21,在比特幣中搏色,PoW的算法叫做Hashcash善茎,和上面的例子是差不多的,礦工們算出結(jié)果之后是會被獎賞的频轿,礦工會在一個交易中收到一個幣垂涯。

PoW算法是很簡單的,那么我們現(xiàn)在的題目就是:
找到一個數(shù)字p航邢,使得hash(pp')的結(jié)果包含是由4個0開頭的耕赘。這里p代表之前的proof,p'是新的proof

...

def PoW(last_proof)
  # proof of work algorithm (PoW)
  proof = 0
  while valid_proof?(last_proof, proof) == false
    proof += 1
  end
  proof
end
...

private
def valid_proof?(last_proof, proof)
  Digest::SHA256.hexdigest("#{last_proof}#{proof}")[0..3] == "0000"
end

如果你需要修改算法的難度膳殷,那么你只需要修改以0開頭的個數(shù)就可以了操骡。

STEP 2

我們這里用 cuba 做一個很小的 web 服務(wù),它主要包含了三個功能

  • POST /transactions/new 生成一筆新的交易
  • GET /mine 告訴服務(wù)器產(chǎn)生一個新的塊
  • GET /chain 把當前鏈返回

node_identifier = SecureRandom.hex(20)
blockchain = Blockchain.new


Cuba.define do
  on get do
    on 'mine' do
      last_block = blockchain.last_block
      last_proof = last_block[:proof]
      proof = blockchain.PoW(last_proof)

      blockchain.new_transaction("0", node_identifier.to_s, 1)

      previous_hash = Blockchain.hash(last_block)
      blk = blockchain.new_block(proof, previous_hash)

      data = {
        message: 'new block forged',
        index: blk[:index],
        transactions: blk[:transactions],
        proof: blk[:proof],
        previous_hash: blk[:previous_hash]
      }
      as_json {{ data: data }}
    end

    on 'chain' do
      as_json do
        {
          data: {
            chain: blockchain.chain,
            length: blockchain.chain.count
          }
        }
      end
    end

  end

  on post do
    on 'transactions/new' do
      on param('sender'), param('recipient'), param('amount') do |sender, recipient, amount|
        index = blockchain.new_transaction(sender,recipient, amount.to_f)

        as_json {{ data: "transaction will be added to block #{index}"}}
      end

      on true do
        as_json 400 do
          { error: 'missing params'}
        end
      end
    end

  end
end

接下來就可以跑一跑我們的簡單的服務(wù)器啦

thin start -p 3000

你可以使用 Postman 或者 curl 來調(diào)用我們的服務(wù)赚窃。

STEP 3

共識册招,這個很重要,在分布式系統(tǒng)中勒极,你需要保證數(shù)據(jù)的一致性是掰,所以你需要知道我們需要通過一種什么樣的算法來保證我們始終指向一條鏈。

  • POST /nodes/register 我們把當前網(wǎng)絡(luò)的節(jié)點都存到一個地方
  • GET /nodes/resolve 這個地方用來解決沖突
def valid_chain?(chain)
  last_block = chain[0]
  current_index = 1

  while current_index < chain.size
    block = chain[current_index]
    puts "count 1"
    # Check that the hash of the block is correct
    if block[:previous_hash] != Blockchain.hash(last_block)
      return false
    end

    # Check that the Proof of Work is correct
    if !valid_proof?(last_block[:proof], block[:proof])
      return false
    end

    last_block = block
    current_index += 1
  end
  return true

end
def resolve_conflicts
  new_chain = nil
  # Only looking for chains longer than this one
  max_length = @chain.count
  aval = @nodes.delete @current_node
  aval.each do |node|
    conn = Faraday.new(url: "http://#{node}/chain")

    res = conn.get do |conn_get|
      conn_get.options.open_timeout = 15
      conn_get.options.timeout = 15
    end
    if res.status == 200
      content = JSON.parse(res.body, symbolize_names: true)
      length = content[:data][:length]
      chain = content[:data][:chain]
      ap "node #{node} len #{length > max_length} valid_chain #{valid_chain?(chain)}"
      if length > max_length && valid_chain?(chain)
        max_length = length
        new_chain = chain
      end
    end
  end

  if new_chain
    puts "found new chain here"
    @chain = new_chain
    return true
  end
  return false
end

# in server.rb

on 'nodes/resolve' do

  blockchain.current_node = "#{env["SERVER_NAME"]}:#{env["SERVER_PORT"]}"
  resolved = blockchain.resolve_conflicts
  if resolved
    data = {
      message: "our chain was replaced",
      new_chain: blockchain.chain
    }
  else
    data = {
      message: "our chain was authorized",
      new_chain: blockchain.chain
    }
  end

  as_json {{ data: data }}
end

valid_chain? 用來判斷這個鏈是否正確的河质,可以看到 resolve_conflicts 里面主要用到的判斷就是鏈長和是否是一條合格的鏈冀惭,如果更長并且合法,那么當前的鏈就替換為該鏈掀鹅。不過這里值得注意的是散休,你需要把nodes中的自己的節(jié)點移除掉,這樣其實是可以提高速度乐尊,并可以解決一個問題戚丸,單線程的機器中,你不能call自己扔嵌。(會有一個OpenTimeOUt的錯誤限府,總是這里你把自己本身的節(jié)點去掉就可以了。)

到這里痢缎,你就可以用另一臺機器或者使用不同的端口來模擬多個節(jié)點胁勺,我這邊 Rakefile 里面默認是通過單機不同端口來模擬多個節(jié)點。

完整的代碼請參考傳送門

你可以開始和小伙伴們玩耍這個蠢蠢的鏈了

希望這個東西能夠給你一些啟發(fā)独旷。

當然啦署穗,最后送上一句話寥裂。

如果你認為技術(shù)能解決安全問題,那么你既不懂安全也不懂技術(shù)案疲。 :)

有很多關(guān)于區(qū)塊鏈的文章都說「區(qū)塊鏈解決的核心問題是信任問題」封恰,但是我沒有看到有人回答了關(guān)鍵的問題:到底什么是信任?什么是所謂「信任問題」褐啡,它存不存在诺舔?什么算是「解決了信任問題」?事實上如果在 Google 上搜一下這句話备畦,會找到大量的復制粘貼和人云亦云低飒。Brice Schneier 書里那句話改一改也是適用的,如果有人認為技術(shù)能解決信任問題懂盐,那么他恐怕既不懂信任也不懂技術(shù)逸嘀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市允粤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌翼岁,老刑警劉巖类垫,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異琅坡,居然都是意外死亡悉患,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門榆俺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來售躁,“玉大人,你說我怎么就攤上這事茴晋∨憬荩” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵诺擅,是天一觀的道長市袖。 經(jīng)常有香客問我,道長烁涌,這世上最難降的妖魔是什么苍碟? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮撮执,結(jié)果婚禮上微峰,老公的妹妹穿的比我還像新娘。我一直安慰自己抒钱,他們只是感情好蜓肆,可當我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布颜凯。 她就那樣靜靜地躺著,像睡著了一般症杏。 火紅的嫁衣襯著肌膚如雪装获。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天厉颤,我揣著相機與錄音穴豫,去河邊找鬼。 笑死逼友,一個胖子當著我的面吹牛精肃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播帜乞,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼司抱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了黎烈?” 一聲冷哼從身側(cè)響起习柠,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎照棋,沒想到半個月后资溃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡烈炭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年溶锭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片符隙。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡趴捅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出霹疫,到底是詐尸還是另有隱情拱绑,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布丽蝎,位于F島的核電站欺栗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏征峦。R本人自食惡果不足惜迟几,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望栏笆。 院中可真熱鬧类腮,春花似錦、人聲如沸蛉加。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至厂抽,卻和暖如春需频,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背筷凤。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工昭殉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人藐守。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓挪丢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親卢厂。 傳聞我的和親對象是個殘疾皇子乾蓬,可洞房花燭夜當晚...
    茶點故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容