一个 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
在一个四人网络中:
初始节点运行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?
# 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
最后祝大家练功顺利, 早日大成.
原文来自:待字闺中
声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com
支持识别各类商场、超市及药店的购物小票,包括店名、单号、总金额、消费时间、明细商品名称、单价、数量、金额等信息,可用于商品售卖信息统计、购物中心用户积分兑换及企业内部报销等场景
涉农贷款地址识别,支持对私和对公两种方式。输入地址的行政区划越完整,识别准确度越高。
根据给定的手机号、姓名、身份证、人像图片核验是否一致
通过企业关键词查询企业涉讼详情,如裁判文书、开庭公告、执行公告、失信公告、案件流程等等。
IP反查域名是通过IP查询相关联的域名信息的功能,它提供IP地址历史上绑定过的域名信息。