在上一篇博文中,我们大致讲解了一下HashGraph共识算法的基本原理,在本节中,我们将讲述虚拟投票过程,并且分析如果有作恶节点,HashGraph共识算法怎样识别并处理。我们还会以上一篇博文件中的场景为例来进行讲解。
系统初始状态如下图所示:
此时华东、华西、华南、华北节点的HashGraph均只有各自的一个节点,且内容完全不一样。假设开始时刻,随机性块定华西节点向华北节点发送Gossip消息。
华西节点首先检查自己的HashGraph,发现其只有一个节点,即最新事件2代表的顶点,没有与华北节点共同的顶点,因此华西节点可以确定,事件2是华北节点所不知道的,因此向华北节点发送Gossip消息如下所示:
华西节点向华北节点发送的消息如下所示:
[
{
“eventId”: e2,
“timestamp”: t2,
"sender": 华西节点,
“txs”: [
{
“txId”: x2,
“vin”: [
{“acct”: 00000, “amt”: 2000}
]
“vout”: [
{“acct”: w2, “amt”: 2000}
]
}
],
“hash": "e2_hash",
“selfParentHash”: “e0_hash”,
“otherParentHash”: “e0_hash”
}
]
华北节点接收到这个消息之后,首先执行事件2中对应的UTXO交易,即向钱二发币2000元,然后在自己的HashGraph上生成新的顶点5,其内容如下所示:
{
“eventId”: e5,
“timestamp”: t5,
"inNode": 华西节点,
"outNode": NULL,
“txs”: [
{
“txId”: x2,
“vin”: [
{“acct”: 00000, “amt”: 2000}
]
“vout”: [
{“acct”: w2, “amt”: 2000}
]
}
],
“hash": "e5_hash",
“selfParentHash”: “e4_hash”,
“otherParentHash”: “e2_hash”
}
此时华北节点的HashGraph则变为如下形式:
接下来华北节点需要给华西节点发送回执信息,此时华北节点遍历自己保存的HashGraph,由事件5开始,其需要找到outNode等于华西节点的顶点,其发现顶点5不能满足要求,同时inNode等于华西节点,因此事件5中的交易是华西节点所知道的,因此不需要保存。于是接着通过selfParentHash找到顶点4,发现同样不能满足要求,而此时已经达到端点,因此得到只有事件4中的交易是需要传递给华西节点的交易。其消息如下所示:
[
{
“eventId”: e4,
“timestamp”: t4,
"inNode": 华北节点,
"outNode": NULL,
“txs”: [
{
“txId”: x4,
“vin”: [
{“acct”: 00000, “amt”: 4000}
]
“vout”: [
{“acct”: w4, “amt”: 4000}
]
}
],
“hash": "e4_hash",
“selfParentHash”: “e0_hash”,
“otherParentHash”: “e0_hash”
}
]
华西节点接收到这个节点之后,首先执行交易4,即向李四发币4000元。接着其生成事件6:
其内容如下所示:
{
“eventId”: e6,
“timestamp”: t6,
"inNode": 华北节点,
"outNode": NULL,
“txs”: [
{
“txId”: x4,
“vin”: [
{“acct”: 00000, “amt”: 4000}
]
“vout”: [
{“acct”: w4, “amt”: 4000}
]
}
],
“hash": "e6_hash",
“selfParentHash”: “e2_hash”,
“otherParentHash”: “e5_hash”
}
此时事件5也需要做出相应的改变:
{
“eventId”: e5,
“timestamp”: t5,
"inNode": 华西节点,
"outNode": 华西节点,
“txs”: [
{
“txId”: x2,
“vin”: [
{“acct”: 00000, “amt”: 2000}
]
“vout”: [
{“acct”: w2, “amt”: 2000}
]
}
],
“hash": "e5_hash",
“selfParentHash”: “e4_hash”,
“otherParentHash”: “e2_hash”
}
此时华西节点的HashGraph结构如下所示:
接着华西节点确定向华东节点发送消息,其从HashGraph的最新事件6开始,向前遍历,找到outNode为华东节点或者到达最老节点为止,当遇到事件6时,其outNode为NULL,因此取出其内交易x4加入到交易栈中,然后找其父节点,这时遇到事件2,事件2的outNode也为NULL,不满足条件,将其中交易x2加入到交易栈中,继续向前找,此时事件2的selfParentHash为空事件,因此意味着已经结束,此时华西节点构造的消息如下所示:
[
{
“eventId”: e2,
“timestamp”: t2,
"inNode": 华西节点,
"outNode": NULL,
“txs”: [
{
“txId”: x2,
“vin”: [
{“acct”: 00000, “amt”: 2000}
]
“vout”: [
{“acct”: w2, “amt”: 2000}
]
}
],
“hash": "e2_hash",
“selfParentHash”: “e0_hash”,
“otherParentHash”: “e0_hash”
},
{
“eventId”: e6,
“timestamp”: t6,
"inNode": 华北节点,
"outNode": NULL,
“txs”: [
{
“txId”: x4,
“vin”: [
{“acct”: 00000, “amt”: 4000}
]
“vout”: [
{“acct”: w4, “amt”: 4000}
]
}
],
“hash": "e6_hash",
“selfParentHash”: “e2_hash”,
“otherParentHash”: “e5_hash”
}
]
华东节点接收到该消息后,首先执行交易队列中的交易,先执行x2再执行x4,然后生成事件7,如下所示:
事件7的内容如下所示:
{
“eventId”: e7,
“timestamp”: t7,
"inNode": 华西节点,
"outNode": NULL,
“txs”: [
{
“txId”: x2,
“vin”: [
{“acct”: 00000, “amt”: 2000}
]
“vout”: [
{“acct”: w2, “amt”: 2000}
]
},
{
“txId”: x4,
“vin”: [
{“acct”: 00000, “amt”: 4000}
]
“vout”: [
{“acct”: w4, “amt”: 4000}
]
},
],
“hash": "e7_hash",
“selfParentHash”: “e1_hash”,
“otherParentHash”: “e6_hash”
}
同时需要更新事件6中内容,将输出节点改为华东节点:
{
“eventId”: e6,
“timestamp”: t6,
"inNode": 华北节点,
"outNode": 华东节点,
“txs”: [
{
“txId”: x4,
“vin”: [
{“acct”: 00000, “amt”: 4000}
]
“vout”: [
{“acct”: w4, “amt”: 4000}
]
}
],
“hash": "e6_hash",
“selfParentHash”: “e2_hash”,
“otherParentHash”: “e5_hash”
}
在这一过程中我们看到,决策过程中需要事件6,而事件6是由华北节点决定的,可以视为华北节点的投票,但是这一过程发生成华西节点中,与华北节点无关,因此我们称这个过程为虚拟投票。
另外,虽然HashGraph是一个有向无环图(DAG),但是在Gossip消息形成过程中,其只需要处理selfParentHash组成的链即可,实际上是否区块链类似的。但是在我们的迷你系统中,有四个节点,因此存在四条并行的链,这样就可以提高系统并行处理能力。
在本篇博文中,我们讲述了HashGraph共识算法中虚拟投票的概念,将上一篇博文中的过程映射到具体的代码实现上。在下一篇博文中,我们首先完成整个消息同步过程,使系统达到新的统一状态。然后我们将讲述系统怎样避免双花问题,例如钱二同时向华西和华北两个节点向赵一转账1500元和800元,在实际应用中只能一笔成功,但是在并行系统下,由于几乎是同时收到这两个请求,我们需要在共识协议中支持避免类似这种双花的功能。