用Ruby实现一个最简的区块链

技术文章 来源:待字闺中 发布:2018-03-01 浏览:1898

摘要:2017年末, 知名的 Blockchainist, 利他主义猛士 Haseeb Qureshi 先生, 发布了一个 Blockchain Demo 视频及代码, 这是一场两个小时的仪式, 是赛博朋克洗礼的圣水.对 Blockchainist 而言, 若未曾实现一个小小的 Demo, 犹如应和了一句古话: 为人不识陈近南,就称英雄也枉然。学习这个小项目, 犹如在你修炼区块神功时, 将 Haseeb 的刚猛内力传入你体内, 虽可以加速修炼进度, 但也存在水土不服, 心情烦躁, 走火入魔的风险.

 一个 Ruby 实现的 Blockchain 导读


Blockchain 是一种革命性的技术, 有些抽象, 比较复杂. 自从比特币诞生以来, 有很多技术的科普文章, 甚至是制精良的动画, 来解释其中的原理. 其中不乏精品之作.

在区块链的世界, 阅读理论的作用是有限的. 离开实践, 力有不逮, 心有余而力不逮, 是很常见的. 从白皮书到代码实现是一个飞跃, 需要一些耐心和技术储备.

积累 Blockchain 的设计细节, 掌握某种编程语言是需要时间的, 适合的学习材料会缩短这个过程. 并且让你妥帖的抚摸区块的皮肤, 感受其链式形体. 

另一方面, 一旦制作出一个完整的  Blockchain 的 Demo, 这项技术瞬间与你的灵魂结合, 留下虔诚的烙印和神圣的宗教代码.

2017年末, 知名的 Blockchainist, 利他主义猛士 Haseeb Qureshi 先生, 发布了一个 Blockchain Demo 视频及代码, 这是一场两个小时的仪式, 是赛博朋克洗礼的圣水.

对 Blockchainist 而言, 若未曾实现一个小小的 Demo, 犹如应和了一句古话: 为人不识陈近南,就称英雄也枉然。

学习这个小项目, 犹如在你修炼区块神功时, 将 Haseeb 的刚猛内力传入你体内, 虽可以加速修炼进度, 但也存在水土不服, 心情烦躁, 走火入魔的风险. 

当你吸入, 消化洋人内力时, 需要本尊这粒药引子, 去腥除骚, 护体保平安.

下面逐层介绍神功的实现目标以及必备知识.


神功第一层 
钱坤大挪移,
谢逊度假夏威夷

这一层实现查账转账功能, 转账功能是根据账户余额, 进行加减计算.

此类功能用 HTTP 协议的 GET 和 POST 实现最适合, GET 用来获取服务端数据, POST 用来更改服务端数据.

这里不需要 HTML 来呈现 UI, 使用 Ruby Web 框架 Sinatra 来组织 URL 和相关的 method, 在命令行终端可以看到转账信息.

客户端的 method 和服务端的 url 都非常简洁

客户端: client.rb

  def create_user(name) … end

  def get_balance(user)  … end

  def transfer(from, to, amount) ... end

服务端: haseebcoin.rb

  get “/balance” ... end

  post “/users” ... end

  post “/transfers” ... end

本层必要知识: ruby, HTTP GET POST, Sinatra


神功第二层 
白玉堂大破铜网阵

奸王私建冲霄楼, 铜网阵, 里面遍布是消息埋伏, 探测环境变化, 传递信息, 触动夺命机关.

<三侠五义> 中描述了一种致命的物联网, 区块链中也有类似的结构, Gossip Protocols 八卦, 不是乾, 坤, 坎, 離… 是八卦新闻的意思, 是去中心化网络.

我们建立一个能够交换电影名称的 Gossip 网络.

client.rb 实现了向某端口送消息

def self.gossip(port, state)
 ...  

 
Faraday.post("#{URL}:#{port}/gossip", state: state).body

 ...

end

gossip.rb 接受两个参数, 源端口和目的端口, 源端口在某个端口说话, 比如端口 1111, 2222.


在实际的去中心网络中, 这俩端口代表两个网络节点, 在本机上不同端口说话, 模拟网络中的不同的节点.

每个节点
每 3 秒, 说出最爱电影名称

every(3.seconds) do

  …

  gossip_response = Client.gossip(port, JSON.dump(STATE))

  update_state(JSON.load(gossip_response))

  ...

end

每 8 秒, 改变最爱电影.

every(8.seconds) do

  …

  update_state(PORT => [@favorite_movie, @version_number])

  ...

end

服务端接收并处理数据

post '/gossip' do

  …

  update_state(JSON.load(their_state))

  …

end

在一个四人网络中:

  1. 初始节点运行gossip.rb 1111, 第一节点在 1111 端口说出最爱电影

  2. 运行 gossip.rb 2222 1111, 第二节点在 2222 端口向第一节点 (1111 端口) 说出最爱电影

  3. 运行 gossip.rb 3333 2222, 第三节点在 3333 端口向第二节点 (2222 端口) 说出最爱电影

  4. 运行 gossip.rb 4444 3333, 第四节点在 4444 端口向第三节点 (3333 端口) 说出最爱电影

运行一段时间后, 最终, 四个节点都有对方的信息, 而且信息在不停的变化. 这就是一个简单的 Gossip 网络.


神功第三层 
倚天屠龙藏玄机, 
武穆遗书加解密

完颜洪烈道:“岳飞无法可施,只得把那部兵书贴身藏了,写了四首甚么《菩萨蛮》、《丑奴儿》、《贺圣朝》、《齐天乐》的歪词。

这四首词格律不对,平仄不叶,句子颠三倒四,不知所云。”

“哪知其中竟是藏着一个极大的哑谜。

小王苦苦思索,终于解明了,原来这四首歪词须得每隔三字的串读,先倒后顺,反复连贯,便即明明白白。”

顶级加密算法是区块链的基石.

这一层使用非对称加密技术, 实现区块链账户.

RSA 算法能够生成公钥, 私钥, 并实现非对称加密功能.

def generate_key_pair … end
def sign(plaintext, raw_private_key) ... end

得益于 Ruby 语言的 openssl module, 我们可以很轻松的实现非对称加密和签名验证等功能.
在区块链中, 公钥是用户名, 私钥是密码, 一对密钥, 就是一个区块链账户.

解密 ciphertext:

def plaintext(ciphertext, raw_public_key) … end

验证 ciphertext 是不是 message:

def valid_signature?(message, ciphertext, public_key) … end

本层必要知识: 非对称加密算法


神功第四层 
老君炉里赖一年, 
酿制区块出金丹

这一层实现了工作证明, 产生了区块链中的区块, 是费时费力的过程.
Hash Function的特点是不可逆和无冲突, 计算过程很简单, 将 input 经过哈希运算, 得到 result,
input 是转账, 钱, 花钱的人, 收钱的人等等信息

哈希运算有很多种算法, 这里使用 SHA256 算法:

def hash(message) … end

同样的信息, 做同样的哈希运算, 会得到不同的 result, 我们不停的做运算, 直到得到的 result 符合某些特性, 比如 result 前几位都是 0.

验证运算结果是不是以几个 0 开始:

def is_valid_nonce?(nonce, message)

  hash(message + nonce).start_with?("0" * NUM_ZEROES)

end

符合以上条件运算执行起来不是那么容易, 需要耗费大量时间, 整个过程被称为挖矿:

def find_nonce(message)

  …  

 until is_valid_nonce?(nonce, message)  ...

end

input 中会包含上一次哈希运算的结果, 所以每次哈希运算都收到了上一次运算的影响, 换句话说, 这是一个链式结构, 也就是区块链的由来.

神功第五层 
区块链节节扣, 
最长链扫乾坤

这一层会初始化第一个区块, 并据此, 生产一个链式结构, 形成区块链. 区块链可以存储在 Array 结构中, 在存储的过程中, 还要验证区块是否有效.

初始化区块 class Block

def initialize(prev_block, msg)

 @msg = msg

 @prev_block_hash = prev_block.own_hash if prev_block
 
 mine_block!
   
end

挖矿, 最繁重的劳动是找 nonce

def mine_block!

 @nonce = calc_nonce

 @own_hash
= hash(full_block(@nonce))

end

一个完整的区块是这样 compact 出来的

def full_block(nonce)

  [@msg, @prev_block_hash, nonce].compact.join

end

初始化区块链 class BlockChain
用 Array 存储就可以啦:

def initialize(msg)
 
 @blocks
= []

 @blocks << Block.new(nil, msg)

end

将区块加入链条, 整个区块链在不停的增长

def add_to_chain(msg)

 @blocks << Block.new(@blocks.last, msg)  puts @blocks.last

end

一定要严格的验证 block 是不是健康

def valid?

 @blocks.all? { |block| block.is_a?(Block) } &&

   @blocks.all?(&:valid?) &&
   
   @blocks.each_cons(2).all? { |a, b| a.own_hash == b.prev_block_hash }
   
end

神功第六层 
六合之法, 融会贯通
朋克神教, 初现神通

所谓六合,“精气神”为内三合,“手眼身”为外三合,

其用为“眼与心合,心与气 合,气与身合,身与手合,手与脚合,脚与胯合。”

全身内外,浑然一体。

此乃少林旁支韦陀门的武功,全守六合之法。

在第一层转账交易 class Transaction 中, 需要用私钥对信息进行签名

@signature = PKI.sign(message, priv_key)

第一个挖出区块, 会的到 500_000 大洋的奖励.

def self.create_genesis_block(pub_key, priv_key)

  genesis_txn = Transaction.new(nil, pub_key, 500_000, priv_key)

 Block.new(nil, genesis_txn)

end

验证账户花钱是不是有效

def all_spends_valid?

  compute_balances do |balances, from, to|

   return false if balances.values_at(from, to).any? { |bal| bal < 0 }

 end  true

end

将未知的节点加入 $PEERS, 保持网络增长

if PEER_PORT.nil?

 # You are the progenitor!  $BLOCKCHAIN = BlockChain.new(PUB_KEY, PRIV_KEY)

 else  # You're just joining the network.  $PEERS << PEER_PORT

end

节点之间的处理数据, 先读取 blockchain 和 peers, 然后更新他们

# @param blockchain

# @param peers

post '/gossip' do  their_blockchain = YAML.load(params['blockchain'])  their_peers = YAML.load(params['peers'])  update_blockchain(their_blockchain)  update_peers(their_peers)  

 YAML.dump('peers' => $PEERS, 'blockchain' => $BLOCKCHAIN)

end

处理接受到的区块, 我们只关心他是不是更长

def update_blockchain(their_blockchain)  

 return if their_blockchain.nil?

 return if $BLOCKCHAIN && their_blockchain.length <= $BLOCKCHAIN.length
 
 return unless their_blockchain.valid?  $BLOCKCHAIN = their_blockchain
 
 end

更新 peers, 只要以前没有的 peer:

def update_peers(their_peers)

 $PEERS = ($PEERS + their_peers).uniq

end

发送钱, 需要先得到对方的 pub_key, 然后从我的 pub_key 向他发送 amount.

# @param to (port_number)

# @param amount

post '/send_money' do  to = Client.get_pub_key(params['to'])  amount = params['amount'].to_i

 $BLOCKCHAIN.add_to_chain(Transaction.new(PUB_KEY, to, amount, PRIV_KEY))

 'OK. Block mined!'


end

区块链放进 Gossip 网络, 将各个功能组合到一起, 一个可运行的 Block Demo 就成功了.

这个 Demo 在 Github 上, 油管上配有视频,

https://github.com/Haseeb-Qureshi/lets-build-a-blockchain

最后祝大家练功顺利, 早日大成.



原    文:待字闺中
作    者:待字闺中

免责声明:

  1. SDK.cn遵循行业规范,所有转载文章均征得作者同意授权并明确标注来源和链接。
  2. 我们十分尊重原创作者的付出,本站禁止二次转载如需转载请与原作者取得联系。
  3. 转载SDK.cn的原创文章需注明文章作者、链接和"来源:SDK.cn"并保留文章及标题完整性未经作者同意不得擅自修改。
  4. 作者投稿可能会经SDK.cn适当编辑修改或补充。
推荐工具 意见反馈