Rust 实现一个简单的区块链
一、背景
近期用 Rust 实现了 Jeiwan/blockchain_go,与原项目相比没有加入新的功能,只是换了一个编程语言实现了一遍,源码放在 Github 上。
开发这个项目,花费了好几个周末,比较低效,需要反思。中途差点烂尾,被情绪影响,不知道做这件事的意义在哪里,有什么收益,还好坚持了下来。我很佩服原项目的作者,能够持之以恒将一个项目做得那么好,还有完整的文档讲解。循序渐进,代码配合文档,非常清晰易懂。换句话说,作者为了写一篇技术科普文章介绍区块链,捎带用代码写了一个演示案例 ————————— 代码是文档的注释
过去一年里,在学习摄影过程中,了解到美术的一个概念:
- 临摹:将别人的作品惟妙惟肖地画出来,这祌方法就是临摹。
- 写生:直接面对实物进行描绘就是写生。
- 创作:是对现实生活通过观察、体验、研究、分析、选择、加工和提炼后,塑造艺术形象的创造性劳动。
(图:https://www.laihuihua.com/K/article-1481.html)
临摹是学习、研究和掌握前人绘画经验的一个桥梁,可以使画者获得一定的绘画技巧,为写生奠定基础。写生是不断提高画者对客观事物的感受能力以及绘画技艺的唯嚙径,为创作积累生动的素材,是创作的必经之路。创作则集中体现了画家的绘画功底和综合艺术修养。临摹、写生和创作三者相互依存,互相促进,缺一不可。
我觉得这个概念同样适合于技术领域,甚至任何行业,以技术领域举例:
- 临摹:学习框架、开源项目的源码,临摹其中的优秀设计,将其用于实际项目。
- 写生:发现一个需求,根据过往的经验,输出技术方案,完成程序开发工作。
- 创作:推动技术创新,改变世界,比如:Google 大数据的三大论文。
好啦,前面啰嗦了那么多,究竟想干啥。其实就是临摹这个项目,同时提高学习内容留存率:
- 通过做实践项目,提高 Rust 编程能力。
- 学习区块链基本概念,理解 BTC 内部实现原理。
二、区块链概念
下面简要摘录下里面使用的名词术语:
区块
在区块链中,真正存储有效信息的是区块(block)。而在比特币中,真正有价值的信息就是交易(transaction)。实际上,交易信息是所有加密货币的价值所在。除此以外,区块还包含了一些技术实现的相关信息,比如版本,当前时间戳和前一个区块的哈希。
区块链
本质上,区块链就是一个有着特定结构的数据库,是一个有序,每一个块都连接到前一个块的链表。也就是说,区块按照插入的顺序进行存储,每个块都与前一个块相连。
工作量证明
区块链的一个关键点就是,一个人必须经过一系列困难的工作,才能将数据放入到区块链中。正是由于这种困难的工作,才保证了区块链的安全和一致。此外,完成这个工作的人,也会获得相应奖励(这也就是通过挖矿获得币)。
这个机制与生活现象非常类似:一个人必须通过努力工作,才能够获得回报或者奖励,用以支撑他们的生活。在区块链中,是通过网络中的参与者(矿工)不断的工作来支撑起了整个网络。矿工不断地向区块链中加入新块,然后获得相应的奖励。在这种机制的作用下,新生成的区块能够被安全地加入到区块链中,它维护了整个区块链数据库的稳定性。值得注意的是,完成了这个工作的人必须要证明这一点,即他必须要证明他的确完成了这些工作。整个 “努力工作并进行证明” 的机制,就叫做工作量证明(proof-of-work)。要想完成工作非常地不容易,因为这需要大量的计算能力:即便是高性能计算机,也无法在短时间内快速完成。另外,这个工作的困难度会随着时间不断增长,以保持每 10 分钟出 1 个新块的速度。
在比特币中,这个工作就是找到一个块的哈希
,同时这个哈希满足了一些必要条件。这个哈希,也就充当了证明的角色。因此,寻求证明(寻找有效哈希),就是矿工实际要做的事情。交易
交易(transaction)是比特币的核心所在,而区块链唯一的目的,也正是为了能够安全可靠地存储交易。在区块链中,交易一旦被创建,就没有任何人能够再去修改或是删除它。
由于比特币采用的是 UTXO 模型,并非账户模型,并不直接存在“余额”这个概念,余额需要通过遍历整个交易历史得来。一笔交易由一些输入(input)和输出(output)组合而来。
奖励
挖矿奖励,实际上就是一笔
coinbase
交易。当一个挖矿节点开始挖出一个新块时,它会将交易从队列中取出,并在前面附加一笔coinbase
交易。coinbase
交易只有一个输出,里面包含了矿工的公钥哈希。UTXO 集
Bitcoin Core 存储设计,是将区块存储在
blocks
数据库,将交易输出存储在chainstate
数据库。其中,chainstate
也就是未花费交易输出的集合,可以理解为blocks
数据库的未花费交易输出的索引,称之为UTXO
集。P2PKH
在比特币中有一个 脚本(Script) 编程语言,它用于锁定交易输出;交易输入提供了解锁输出的数据。它所做的事情就是向一个公钥哈希支付,也就是说,用某一个公钥锁定一些币。这是比特币支付的核心:没有账户,没有资金转移;只有一个脚本检查提供的签名和公钥是否正确。
有了一个这样的脚本语言,实际上也可以让比特币成为一个智能合约平台:除了将一个单一的公钥转移资金,这个语言还使得一些其他的支付方案成为可能。
钱包地址
比特币地址是完全公开的,如果你想要给某个人发送币,只需要知道他的地址就可以了。但是,地址并不是用来证明你是一个“钱包”所有者的信物。实际上,所谓的地址,只不过是将公钥表示成人类可读的形式而已,因为原生的公钥人类很难阅读。在比特币中,你的身份(identity)就是一对(或者多对)保存在你的电脑(或者你能够获取到的地方)上的公钥(public key)和私钥(private key)。比特币基于一些加密算法的组合来创建这些密钥,并且保证了在这个世界上没有其他人能够取走你的币,除非拿到你的密钥。
数字签名
在数学和密码学中,有一个数字签名(digital signature)的概念,算法可以保证:
- 当数据从发送方传送到接收方时,数据不会被修改;
- 数据由某一确定的发送方创建;
- 发送方无法否认发送过数据这一事实。
通过在数据上应用签名算法(也就是对数据进行签名),你就可以得到一个签名,这个签名晚些时候会被验证。生成数字签名需要一个私钥,而验证签名需要一个公钥。签名有点类似于印章,比方说我做了一幅画,完了用印章一盖,就说明了这幅画是我的作品。给数据生成签名,就是给数据盖了章。
区块链网络
区块链特性可以认为是规则(rule),区块链网络就是一个程序社区,里面的每个程序都遵循同样的规则,正是由于遵循着同一个规则,才使得网络能够长存。
区块链网络是去中心化的,这意味着没有服务器,客户端也不需要依赖服务器来获取或处理数据。在区块链网络中,有的是节点,每个节点是网络的一个完全(full-fledged)成员。节点就是一切:它既是一个客户端,也是一个服务器。这一点需要牢记于心,因为这与传统的网页应用非常不同。
区块链网络是一个 P2P(Peer-to-Peer,端到端)的网络,即节点直接连接到其他节点。它的拓扑是扁平的,因为在节点的世界中没有层级之分。
节点角色
尽管节点具有完备成熟的属性,但是它们也可以在网络中扮演不同角色。比如:
- 矿工 这样的节点运行于强大或专用的硬件(比如 ASIC)之上,它们唯一的目标是,尽可能快地挖出新块。矿工是区块链中唯一可能会用到工作量证明的角色,因为挖矿实际上意味着解决 PoW 难题。在权益证明 PoS 的区块链中,没有挖矿。
- 全节点 这些节点验证矿工挖出来的块的有效性,并对交易进行确认。为此,他们必须拥有区块链的完整拷贝。同时,全节点执行路由操作,帮助其他节点发现彼此。对于网络来说,非常重要的一段就是要有足够多的全节点。因为正是这些节点执行了决策功能:他们决定了一个块或一笔交易的有效性。
- SPV SPV 表示 Simplified Payment Verification,简单支付验证。这些节点并不存储整个区块链副本,但是仍然能够对交易进行验证(不过不是验证全部交易,而是一个交易子集,比如,发送到某个指定地址的交易)。一个 SPV 节点依赖一个全节点来获取数据,可能有多个 SPV 节点连接到一个全节点。SPV 使得钱包应用成为可能:一个人不需要下载整个区块链,但是仍能够验证他的交易。
三、节点网络事件
注:下面是一个简化的网络模型,用于模拟多节点网络。笔者没有研究过 BTC 的P2P网络,不了解真实的P2P网络通讯过程。
在简化后的网络模型中,节点之间采用异步消息通信机制。将每个消息称为网络事件,梳理的事件机制如下:
四、项目介绍
源码地址:https://github.com/ZuoFuhong/blockchain_rust
前面提到,Rust 实现的版本,仅仅是编程语言不同于原项目,其它的逻辑均是一致的。如果您阅读过源码,还是会发现一丢丢的不同。
- 单元测试:Rust 编码有个很爽的点,就是可以在同一个源码文件中写单元测试,运行单元测试。不用像 Golang 那样需要在不同文件中切换。所以可以看到,项目源码中有着非常多的单元测试。
- 命令行程序:由于语言上的差异,命令行的实现差异较大,可以看到 Rust 的版本使用因语言特性,使用属性宏整体更加简洁。
- 区块链 db / 钱包文件:区块数据和钱包数据会持久化到磁盘上,相比原项目,Rust 的版本没有给文件打上端口号,减少源码阅读干扰。
五、开始游戏
我很乐于将 BTC 称之为游戏,每个节点有自己的身份角色,也可以切换角色。通过网络,大家遵守着同样的游戏规则。玩家穿行于虚拟和现实,在现实世界中赚取游戏中的虚拟货币,用游戏中的虚拟货币进行现实消费。Wow! That's totally awesome!
好啦,现在开始一场游戏吧 !
1.在同一台机器上,使用三个不同的端口,模拟三个节点:
其中:
- node1: 中心节点
- node2: 钱包节点
- node3: 矿工节点
2.在 node1 中心节点下创建一个钱包和一个新的区块链
生成一个仅包含创世块的区块链,并在其他节点使用。创世块承担了一条链标识符的角色(在 Bitcoin Core 中,创世块是硬编码的)
其中:
- 钱包地址:1QAjnwyGZL3woxUvzhdVePyjThtxQYpogL
- 创世块 hash:00e2b7601305ffe6ac39a7272324d42d3de2cf6f68129c9f555127c8ccb94b7f
3.接下来,在 node2 钱包节点生成一些钱包地址,我们称这些地址叫做:
- WALLET_1:1DB3GnPvCXEeLyzL9FxZHNzUz6C2eQFxvN
- WALLET_2:19g2ZZBiVCafgTQjUrEmJnwGgoc16nk1Yi
- WALLET_3:14A4DXBji2pnZRmXL97dVGVDe2ckuVcGGh
4.在 node1 中心节点,向钱包地址发一些币:
其中:
# 创世块矿工地址,向 WALLET_1 发 10 个币
$ blockchain_rust send ${CENTREAL_NODE} ${WALLET_1} 10 1
# 创世块矿工地址,向 WALLET_2 发 10 个币
$ blockchain_rust send ${CENTREAL_NODE} ${WALLET_2} 10 1
5.在 node1 中心节点,运行节点
$ blockchain_rust startnode
6.在 node2 钱包节点,运行节点
$ blockchain_rust startnode
它会从中心节点(node1)下载所有区块。为了检查一切正常,暂停节点运行并检查余额:
$ blockchain_rust getbalance ${WALLET_1}
Balance of 'WALLET_1': 10
$ blockchain_rust getbalance ${WALLET_2}
Balance of 'WALLET_2': 10
7.在 node3 矿工节点中生成一个钱包地址。初始化区块链:
# 将钱包地址,作为矿工钱包
$ blockchain_rust startnode ${MINER_WALLET}
其中:
- 钱包地址:1EwpTmQ941b1B7VMxzbVXvc8CrXR89dNXM
8.在 node2 钱包节点,发送一些币:
# WALLET_1 地址,向 WALLET_3 发 2 个币
$ blockchain_rust send ${WALLET_1} ${WALLET_3} 2 0
# WALLET_2 地址,向 WALLET_3 发 3 个币
$ blockchain_rust send ${WALLET_2} ${WALLET_3} 3 0
迅速切换到矿工节点(node3),你会看到挖出了一个新块!同时,检查中心节点(node1) 的输出。
9.切换到 node2 钱包节点并启动
它会下载最近挖出来的块!
暂停节点并检查余额:
Done !
Rust 实现一个简单的区块链的更多相关文章
- Python创建一个简单的区块链
区块链(Blockchain)是一种分布式账本(listributed ledger),它是一种仅供增加(append-only),内容不可变(immutable)的有序(ordered)链式数据结构 ...
- [Python Study Notes]一个简单的区块链结构(python 2.7)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ...
- 只用120行Java代码写一个自己的区块链
区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大家都非常想了解这一切是如何工作的.这篇文章就是帮助你使用 Java 语言来实现一个简单的区块链,用不到 120 行代码来揭示区 ...
- 只用200行Go代码写一个自己的区块链!
Coral Health · 大约23小时之前 · 220 次点击 · 预计阅读时间 7 分钟 · 不到1分钟之前 开始浏览 区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大 ...
- 用 Python 构建一个极小的区块链
虽然有些人认为区块链是一个早晚会出现问题的解决方案,但是毫无疑问,这个创新技术是一个计算机技术上的奇迹.那么,究竟什么是区块链呢? 区块链 以比特币(Bitcoin)或其它加密货币按时间顺序公开地记录 ...
- 只用120行Java代码写一个自己的区块链-3挖矿算法
在本系列前两篇文章中,我们向大家展示了如何通过精炼的Java代码实现一个简单的区块链.包括生成块,验证块数据,广播通信等等,这一篇让我们聚焦在如何实现 PoW算法. 大家都无不惊呼比特币.以太坊及其他 ...
- 用Java实现简单的区块链
用 Java 实现简单的区块链 1. 概述 本文中,我们将学习区块链技术的基本概念.也将根据概念使用 Java 来实现一个基本的应用程序. 进一步,我们将讨论一些先进的概念以及该技术的实际应用. 2. ...
- 只用200行Go代码写一个自己的区块链!(转)
区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大家都非常想了解这一切是如何工作的.这篇文章就是帮助你使用 Go 语言来实现一个简单的区块链,用不到 200 行代码来揭示区块链 ...
- 300行ABAP代码实现一个最简单的区块链原型
不知从什么时候起,区块链在网上一下子就火了. 这里Jerry就不班门弄斧了,网上有太多的区块链介绍文章.我的这篇文章没有任何高大上的术语,就是300行ABAP代码,实现一个最简单的区块链原型. 我个人 ...
随机推荐
- JetBrains又出神器啦!Fleet,体验飞一般的感觉
目录 简介 从eclipse到Fleet Fleet的特性 JetBrains Space 总结 简介 java开发的同学可能对于JetBrains这家公司并不陌生,因为JetBrains号称拥有世界 ...
- win10 1909+ vs2015up3 使用fmt概述(fmt version 7.0.1)
!!版权声明:本文为博主原创文章,版权归原文作者和博客园共有,谢绝任何形式的 转载!! 作者:mohist fmt 源码: https://github.com/fmtlib/fmt fmt官方文档: ...
- c++基础之operator =处理
1.注意自我赋值 先看个例子: class A {}; A a ; a = a; // 注意这句 可能实际中,你不会这样做,但是实际中,是有可能的,并且这样做,也不违背语法. BUT, 如果上面的例子 ...
- java源码——计算不同图形的周长和面积
计算任意三角形,正方形,正五边形,圆形的周长和面积. 利用类的继承实现. 将计算结果进行输出. 不多说,贴码. Contants.java 常量存储类 <pre name="code& ...
- Robin Hood
Robin Hood 题目链接 题意 给你n个人和他们的钱数,然后给你k天,每天可以从最高钱数的人那边取一块钱给最少钱数的人,问最后钱数最多的人和钱数最少的人相差多少: 思路 二分最钱数,能下降到的位 ...
- LeetCode1240铺瓷砖
题目 n*m的矩阵,只用正方形铺.求最少正方形个数. n,m<=13 思路 贪心: 加入是最大的正方形,显然行不通,比如n=11,m=13.那么贪心策略是1个11,其余是大小为2的正方形5个,大 ...
- Doing Homework(hdu)1074
Doing Homework Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)To ...
- codeforce-424C. Magic Formulas(数学)
C. Magic Formulas time limit per test:2 seconds memory limit per test:256 megabytes input stan ...
- 1052 - String Growth
1052 - String Growth PDF (English) Statistics Forum Time Limit: 2 second(s) Memory Limit: 32 MB Z ...
- The Balance(poj2142)
The Balance Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 5452 Accepted: 2380 Descr ...