一个 Ruby 实现的 Blockchain 导读
Blockchain 是一种革命性的技术, 有些抽象, 比较复杂. 自从比特币诞生以来, 有很多技术的科普文章, 甚至是制精良的动画, 来解释其中的原理. 其中不乏精品之作.
在区块链的世界, 阅读理论的作用是有限的. 离开实践, 力有不逮, 心有余而力不逮, 是很常见的. 从白皮书到代码实现是一个飞跃, 需要一些耐心和技术储备.
![LXUJdOsrVqnhbgu4skaX.png]()
积累 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
在一个四人网络中:
-
初始节点运行gossip.rb 1111, 第一节点在 1111 端口说出最爱电影
-
运行 gossip.rb 2222 1111, 第二节点在 2222 端口向第一节点 (1111 端口) 说出最爱电影
-
运行 gossip.rb 3333 2222, 第三节点在 3333 端口向第二节点 (2222 端口) 说出最爱电影
-
运行 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?
$BLOCKCHAIN = BlockChain.new(PUB_KEY, PRIV_KEY)
else
$PEERS << PEER_PORT
end
节点之间的处理数据, 先读取 blockchain 和 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.
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
最后祝大家练功顺利, 早日大成.