终于这个系列要翻译完了,原文链接

前言

目前为止,我们构建了区块链的很多关键特性,匿名,安全,随机生成地址。区块链数据存储;POW工作证明;可靠的交易存储。这些特征很重要,但是还不够。真正让这些特征闪光,使得加密货币成为可能。但是,让区块链执行在一个节点上有什么用?当系统只有一个用户的时候,这些特性有什么用?网络使得这些机制工作起来。


你可以把区块链特性当作原则,类似于人类社交的原则。区块链网络是一系列的遵循相同规则的程序。类似的,当人们分享想法时,他们的生活过的更好。如果有人遵循其他规则,他们生活在一个独立的社会。同样,如果区块链节点遵循不同的规则,他们形成另一个网络。

这非常重要:没有网络和相同的规则约束,区块链没有用处!


声明:不幸的是,我没有时间实现真正的P2P网络。本文我描述大多数常用场景,包含不同类型的节点。完善场景实现真的P2P网络对读者来说是一个不错的实战挑战!我并不保证其他场景可以工作。


区块链网络

区块链网络是去中心化的,这意味着没有服务器和客户端去获取和处理数据。区块链中有节点,并且每个节点都是网络的全数据节点。节点即一切:是客户端也是服务器。要时刻记住这点,这与一般网络应用完全不同。

区块链是一个P2P网络,每个节点都与其他节点相连接。其拓扑是扁平的。语义如下:


这个网络中节点很难实现,因为它们需要执行一系列的操作。每个节点必须和其他多个节点交互,必须请求其他节点状态,并和自身状态进行比对,在自己过时时进行更新。


节点规则

尽管是一个全节点,区块链中的节点可以在网络中充当不同的角色。例如:

1、矿工

这些节点运行在一个算力强大的硬件(例如asic),他们的目标是尽可能快的挖矿。矿工是区块链中唯一使用pow的校色,因为挖矿意味着解决pow谜底。在POs区块链中,没有挖矿。

2、全节点

这些节点对矿工挖出来的区块进行验证,为了实现这个功能,他们必须有区块的完整拷贝。同时,这些节点实现路由操作,例如帮助其他节点发现彼此。这对一i个包含海量节点的网络非常重要,因为这些节点进行决策,他们对区块或者交易的正确性进行验证。

3、SPV节

SPV表示简单支付验证。这些节点不存储完整的区块,但是他们仍然可以验证交易。一个SPV节点依赖全数据节点获取数据。网络中可能有许多SPV节点连接全节点。钱包可以使用SPV来实现:不需要下载整个区块,仍然可以验证交易。


网络简化

为了实现区块链中的网络,我们需要简化一些东西。问题是我们没有足够多的电脑用来模拟多节点。我们使用docker或者虚拟机来解决问题,但这会使得问题更加复杂:必须解决docker或者虚拟机问题,而我们只是想实现区块链。因此,我们希望一个机器上运行多个区块链节点并使用不同的地址。为此,我们使用端口作为节点标识。例如,节点有127.0.0.1:3000。127.0.0.1:3001,127.0.0.1:3002等等。我们成端口节点ID并使用环境变量NODEID设置。你可以打开多个终端窗口,并设置不同的NODE_ID运行。

该方法同样需要不同的区块和钱包文件。他们目前以来节点ID并使用block_3000.db和block_3001.db,wallet_3000.db和wallet_30001.db命名。


实现

所以,当运行比特币节点时,节点必须连接至某个节点并下载最新的区块链状态。考虑你的电脑可能并不知道这些节点信息。

将节点硬解码至bitcoin core可能会导致错误,这些节点可能被攻击或者关闭,导致新的节点不能加入网络。bitcoin core使用dns seeds来硬解码。它们并不是节点,但是dns服务器知道节点的地址。当你启动一个新的bitcoin core时,它将连接至所有种子节点并获得全节点地址,你将从这些节点下载数据。

在我们的实现中,它们时中心化的,我们有三个节点:

1、中心节点。所有节点连接至该节点,该节点和其他节点进行数据交互。

2、挖矿节点。该节点存储交易于内存池,当有足够的交易,它将成为一个新块。

3、钱包节点。该节点被用来钱包之间转账。与SPV节点不同,他们保存整个区块链。

场景

本文实现以下场景:

1、中心节点创建区块链

2、气态节点连接该节点并下载区块链

3、一个挖矿节点连接至中心节点并下载区块链

4、一个钱包节点创建交易

5、挖矿收到交易后保存至内存池

6、当内存池中有足够多的交易时,矿工节点启动挖矿。

7、当新块被挖出来时,发送至中心节点

8、钱包节点与中心节点同步数据

9、钱包节点用户检查支付是否成功。

这看起来很想比特币。尽管我们不打算构建真是的P2P网络,我们打算实现比特币的大部分用例。

版本

节点通过消息交互。当一个新节点运行时,从dns获取一系列的节点,并交换版本信息:

节点收到版本信息后使用自身版本信息响应。这类似于握手的过程,在这之前没有任何信息交互。版本用来寻找更长的区块。当一个节点收到版本信息后检查区块高度。若本地节点缺失新的区块,启动下载过程。

为了收到缺失的信息,我们需要一个服务器:


首先,我们需要将地址硬码至中心节点:初始时,每个节点必须知道连接目的。mineraddress参数指定挖矿奖励的地址。如下所示:


这意味着如果当前节点不是中心节点,它必须发送版本信息至中心节点确定自己是否过时:


我们的消息,在底层是一串bytes。前12个bytes指定命令名称(这里是版本),节点来包含编码结构。commandToBytes如下:


这里创建12byte换成并用命令名称填满,空余的为empty。下面函数功能相反:


当一个节点收到命令时,它运行bytesToCommand提取命令并处理:

版本命令hander如下:


首先,我们需要解码请求。所有handler都类似,我们忽略其他的部分。

然后节点比对其BestHeight,如果节点区块较长,它返回版本信息。否则,其发送getblocks消息。

getblocks


getblocks意味着”向我展示你的区块“(比特币中稍微复杂些)。注意,这并不是说”给我你的所有区块“,它只是请求区块哈希列表,这可以减少网络复杂,因为区块可以从其他节点下载,我们不希望大量数从一个节点下载。

命令处理很简单:

在我们的实现中,它返回区块的hash值

inv


我们使用inv去向其他节点展示当前节点有的区块和交易。注意,其并不包含整个区块和交易,它只是一些hashes。type说明它们时区块还是交易。

inv的处理稍微复杂:

若发送一个区块hash,我们希望将其保存至blocksInTransit变量来追踪下载的区块。这使得我们可以从不同节点下载区块。在将区块保存至临时变量后,我们发送getdata命令去inv的发送者并更新blocksInTransit。在真正的P2P网络中,我们希望从不同的节点传递区块。

在我们的实现中,我们从不在一个inv中发送多个hashes。因此,payload.Type == "tx"只针对第一个hash。然后我们检查内存池中是否已经有该hash,若没有,发送getdata。

getdata

getdata


处理函数很直观:若它们请求一个区块,则返回,请求交易返回交易。注意到,我们并不检查我们是否包含这个区块或者交易。这是一个bug。

区块和tx:

这些信息用来传递数据

区块的处理很简单:


当我们收到一个新的区块时,将其加入区块链。若没有更多的区块下载,我们从同一个节点下载之前的区块。当我们下载了全部区块,reindex UTXO集合。

TODO: .我们需要在将区块上链之前验证每个收到的区块

TODO: 我们使用UTXOSet.Update(block)而不是UTXOSet.Reindex()。这是因为reindex 整个utxo集合非常浪费时间

tx

首先需要将新交易入内存池(加入前先验证)。接下来:


确定当前节点是否为中心节点,在我们的实现中,中心节点并不挖矿,它仅仅转发交易至网络中的其他节点。


下一个大段为挖矿节点,我们将其拆分为:


miningAddressis是挖矿节点上的集合。当当前节点内存池中有2个或者更多交易时启动挖矿:


首先,验证内存池中所有交易。忽略无效交易,如果没有有效交易,中断挖矿:


验证后的交易和coinbase交易加入区块。在挖矿之后,reindex utxo集合。

TODO: 使用UTXOSet.Update而不是UTXOSet.Reindex

在交易挖矿后,将其从内存池中移除。每个节点都收到当前节点的inv消息,其包含新区块的hash。它们可以在收到消息后请求区块。

结果

回到之前的场景

首先,设置NODE_ID为3000,下文用NODE 3000 或者 NODE 3001

创建钱包和新区块。

简单起见,我使用了假的地址

之后,区块包含简单的创始区块。我们需要保存并在其他节点中使用。创始区块时区块链的标识。

NODE 3001

然后我们开一个新窗口运行NODE 3001。这是一个钱包节点。使用blockchain_go createwallet生辰一些新的地址。我们称其为WALLET_1,WALLET_2,WALLET_3。

NODE 3000
发送


-mine

初始化节点:


节点需要保持运行直接场景结束。

NODE 3001
使用

运行节点:

它将从中心节点下载所有区块。为了验证其是否ok,停止节点并查询余额:


此外,你可以查询中心节点地址余额,因为3001节点包含了它的区块:

NODE 3002


打开一个新窗口设置其id为3002,生成一个钱包,这个挖矿节点,初始化区块链:

启动节点:

NODE 3001

发送一些coins:

NODE 3002

很快,切换至挖矿节点看见挖一个新区块!检查中心节点输出。

NODE 3001

切换至钱包节点:

将下载新挖的区块!

停止并查询余额


搞定!

结论

结论

这是这个系列的最后一篇。我发布了P2P网络中的一些重要信息,但没时间搞定。希望文章对你有所帮助。比特币还有很多新颖的技术!好运!