Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
之前不知道有中文版,看英文版看了几天都没有看完。后来实在受不了从网上找到了中文版,效率提高了很多,囧。

## 阅读之前

针对混合共识机制,在看白皮书的时候,有以下一些问题:POW如何进行选举PBFT节点?PBFT节点的个数?如何判断PBFT节点出现问题?如何确保作恶节点不会超过三分之一?如何进行监督?

希望能够从黄皮书中得到解答。

## 介绍

> TrueChain,一种混合共识协议,它结合了一种改进版的PBFT(实用拜占庭)和POW(工作量证明)共识。POW共识确保了激励和委员会的选举,而PBFT层则承担一种具有瞬时处理高吞吐量事物,交易验证,公平交易贸易委员会的成员轮值功能的高效共识机制,以及作为一种补偿基础设施去处理不同的基础设施。

*[注] 这是对TrueChain共识机制的大致描述:POW+PBFT。可见POW处理更为底层的事务:激励和选举,运用了传统POW机制的优点:不可篡改、更多的参与者(如挖矿节点)等。而提高TPS的任务就交给了PBFT,处理交易信息等。*

## 背景

> PoW协议选择BFT委员会成员的依据是csize(挖出的区块数量)和节点权益的结合。这就提供了一种必要的准入系统,以处理动态的成员以及在许可的环境下切换委员会。

*[注] 这里大概讲解了一下POW选举的依据。不过还是很模糊,不是很清楚如何进行的选举。只是知道了两个影响的参数:csize和节点权益。*

## 共识

> 我们采用水果链作为慢链(snailchain),用以代替Nakamoto(传统链),以抵御1/3−ε腐败(inhashpower)随机小常数ε,以获得最优的适应性。

*[注] 这里的水果链应该就是本文中提到的POW。其如何来抵御1/3−ε腐败,需要查看水果链的相关文档了。*

> 在DailyBFT中,委员会成员运行一个离线的BFT实例来决定每天的日志,而非成员则统计委员会成员的签名数量。

*[注] 这是DailyBPT的架构,分为委员会成员和非成员。两者之间的协作形式还是不确定。
[问题] 每天的日志记录什么内容?有什么作用?签名数量有什么作用?*

> 子协议在节点是BFT委员会成员时的宫作方式: 分叉一个BFT虚拟节点。这里的BFT虚拟节点由BFTpk表示,然后开始接收TXs(交易)。如果停止信号由至少三分之一的初始委员会特定公钥签名,则检查日志完成结束。在此期间,将进行连续的“直到完成”检查,一旦每个步骤都完成了gossip,所有停止日志条亩将被删除。
当节点不是BFT成员时,子协议的工作原理如下: 在接收交易时,消息被添加到历史记录中,并由三分之一的初始委员会特定公钥签名。

> BFT委员会成员在一段特定的时间后进行换届(以慢链作为逻辑时钟)。新的委员
会是由慢链内部产生最新csize区块的矿工组成的。
我们仍然保持在固定时间点强制切换委员会的设计,但频率要低得多(例如,每K天进行一次委员会换届)。另一方面,我们结合了来自Thunderella 的经过认证的投诉的观点,其中慢链可以作为BFT委员会成员不当行为的证据。也就是说,每当委员会的不当行为从慢链中被发现时,第二天的起始点(不一定是第k天)就会触发委员会强制换届。

*[注] (1) 委员会节点由慢链内部的矿工组成。(2) 换届有两种方式:一种固定时间的强制换届,一种由不当行为触发的特定的换届。这种委员会的换届方式区别于DPOS:DPOS由持有人进行直接选举有能力的节点,节点有一定的门槛要求。而这里是节点进行选举,且从矿工中选举。相对DPOS的节点要求较低。*

Algorithm 1: Extra Verification Regarding Physcal Timestamp
算法1: 根据物理时间戳的额外的验证
Data: Input Transaction TX
数据:输入的交易TX
Result: A Boolean value that indicates whether the verification is passed
结果:指示验证是否通过的一个布尔值
1 current time ← Time.Now();
2 if |current time - TX.Tp| > TΔ then
3 return false;
// if the time skew is too large, reject TX .
// 如果时钟漂移太大(时钟偏差太大),则拒绝交易
4 var txn_history = new static dictionary of lists;
5 if txn_history[TX.from] == NULL then
6 txn_history[TX.from] == [TX ];
7 else
8 if txn_history[TX.from][-1].Tp - TX.Tp > 0 then
9 return false;
// To make sure the transactions from the same node preserve timing order.
// 前一个交易的时间晚于后一个交易的时间,则返回false。这是确保交易的时间顺序
10 else
11 txn_history[TX.from].append(TX);
12 return true;

*[注] Tp是物理时间戳,客户端会把物理时间戳Tp放到交易的元数据中。
TΔ是启发式参数,是一个时钟漂移的最大值
上述的算法比较简单:如果超过最大时钟漂移,则认为验证失败。如果之前的交易时间晚于当前的交易的时间,则认为验证失败。*

> 在混合共识中,DailyBFT委员会被索引为一个决定序列DailyBFT[1…R]。
我们用分片St表示第t个DailyBFT委员会序列,为了简单起见,我们将碎片的数量固定为c。**每个DailyBFT都是一个普通的分片**。
我们还有一个由csize节点组成的**主分片Sp**。主分片的任务是最终**确定正常分片输出的顺序,并在分布式交易处理系统中实现协调**。
正常的片,不是直接连接到混合共识的部分,而是提交日志
到原始分片,而原始分片反过来又与混合共识连接。

*[注] 两种分片形式:普通分片和主分片。
[问题] 这里的原始分片是什么?普通分片与混合共识的连接为什么要经过中间的原始分片?这用有什么好处?*

> 我们将状态数据(按帐户范围)均匀地划分为 C个分片。这将确保对相应分片的每个查询都将返回一致的状态。

*[注] 这里的状态是什么?是指原来划分之前的状态数据,还是其他的。如果是之前的状态数据,则容易理解些。*

> 每个数据分区DS[addr]都有rts、wts、reader、writer的元数据。

*[问题] 这里的rts和wts是什么?*

Algorithm 2: Sharding and Speculative Transaction Processing
算法2:分片和投机交易的处理
1 On BecomeShard:
要称为分片
2 Initialize all the state data sectors: lastReaderTS = -1, lastWriterTS = -1, readers = [], writers = []
初始化所有的状态数据: lastReaderTS = -1, lastWriterTS = -1, readers = [], writers = []
3 With transaction TX on shard Si :
在分片Si 上的交易TX
4 On Initialization:
5 TX.lowerBound = 0;
6 TX.upperBound = +∞;
7 TX.state = RUNNING;
8 TX.before = [];
9 TX.after = [];
10 TX.ID = rand;
// 读地址:如果通过地址判断是自己的分区则调用readRemote函数。否则,进行广播出去给对应的分区,直到截止时间到达
11 On Read Address(addr):
12 if host(addr) == Si then
// 我们可以通过调用函数host(addr)获得它的主机分片。
13 Send readRemote(addr) to itself;
14 else
15 Broadcast readRemote(addr, TX.id) to host(addr);
16 Async wait for 2f + 1 valid signed replies within timeout To ;
//在截止时间内等待2f + 1 个有效的签名回复
17 Abort TX when the timeout ticks;
//当截止时间达到时,终止交易
18 Let val, wts, IDs be the majority reply;
// 让 val, wts, IDs是多数的回复
19 TX.before.append(IDs);
20 TX.lowerBound = max(TX.lowerBound, wts);
// wts的作用?这里和 TX.lowerBound比较最大值来设定为 lowerBound
21 return val
22 On Write Address(addr):
23 if host(addr) == Si then
24 Send writeRemote(addr) to itself;
25 else
26 Broadcast writeRemote(addr, TX.id) to host(addr);
27 Async wait for 2f + 1 valid signed replies within timeout To ;
28 Abort TX when the timeout ticks.
29 Let rts, IDs be the majority reply;
30 TX.after.append(IDs) TX.lowerBound = max(TX.lowerBound, rts);
31 return;
32 On Finish Execution: for every TX′inTX .before do
// 这里的TX′是什么?是指交易的分片吗?
33 TX.lowerBound = max(TX.lowerBound, TX’.upperBound);
34 for every TX′inTX .after do
35 TX.upperBound = min(TX.upperBound, TX’.lowerBound);
36 if TX.lowerBound > TX.upperBound then
37 Abort TX;
38 Broadcast Precommit(TX .ID, (TX .lowerBound+TX .upperBound)/2 ) to all the previous remote shards which TX has accessed;
// If TX.upperBound = ∞, we can set an arbitrary number larger than TX .lowerBound.
// 如果 TX.upperBound 是无穷大,我们设定一个大于TX .lowerBound的随机值
// 不确定这里的 Precommit的含义
39 On receive readRemote(addr, ID):
40 if host(addr) == Si then
41 DS[addr].readers.append(ID);
42 return DS[addr].value, DS[addr].wts, DS[addr].writers;
43 else
44 Ignore
45 On receive writeRemote(addr, ID):
46 if host(addr) == Si then
47 DS[addr].writers.append(ID);
48 Write to a local copy;
49 return DS[addr].rts, DS[addr].readers;
50 else
51 Ignore

*[注] 这是在Precommit之前的操作。读操作是将ID放到了DS[addr].readers里面,将ID放到了TX.before里。写操作是将ID放到了DS[addr].writers里面,将ID放到了TX.after里。因此函数处理两个实体变量:交易TX和数据分区DS。最终将TX.ID和TX的边界的中间值送入到Precommit函数中*
接下来是关于Precommit函数:

Algorithm 3: Sharding and Speculative Transaction Processing (cont.)
算法3:分片和投机交易的处理(续)
1 On receive Precommit(ID, cts):
2 Look up TX by ID;
3 if Found and cts not in [TX.lowerBound, TX.upperBound] then
// 如果发现cts不在TX的大小区间内
4 Broadcast Abort(ID) to the sender’s shard.;
// 广播终止函数 Abort(ID)到发送者的分片上
5 TX.lowerBound = TX.upperBound = cts;
6 For every data sector DS[addr] TX reads, set DS[addr].rts = max(DS[addr].rts, cts);
7 For every data sector DS[addr] TX writes, set DS[addr].wts = max(DS[addr].wts, cts);
8 Broadcast Commit(ID, batchCounter) to the sender’s shard.;
// batchCounter is a number which increases by 1 whenever the shard submit a batch of log to the primary shard.
// batchCounter是一个数,每次分片提交一组log到主节点的时候加一
9 On receive 2f + 1 Commit(ID, batchCounter) from each remote shards which TX has accessed:
// 当从交易到达的远端分片收到2f + 1个Commit函数时
10 TX.lowerBound = TX.upperBound = cts;
11 For every data sector DS[addr] TX reads, set DS[addr].rts = max(DS[addr].rts, cts);
12 For every data sector DS[addr] TX writes, set DS[addr].wts = max(DS[addr].wts, cts);
13 Mark TX committed;
14 Let TX .metadata = [ShardID, batchCounter];
15 On output log
16 Sort TX ’s based on their cts. Break ties by physical timestamp.
// 按照子交易的cts来进行对交易进行排序。通过物理时间戳打破联系?

*[注] 这里的cts是交易TX的边界的中间值:(TX .lowerBound+TX .upperBound)/2。设置数据分区DS的trs和wts,并调用Commit函数。在Commit函数中,标记交易已经Committed,并且写入元数据metadata。并对交易进行排序。*

## 智能合约

> 初链基础架构将整合EVM和类似EVM字节码执行引擎来运行智能合约。我们会使用一个虚拟机来处理POW共识,另外一个虚拟机处理PBFT共识,都集成在全节点中,因此它们可以处理按需调用。

*[注] 因为是混合共识机制,对应的只能合约有对应的两个虚拟机来承载,一个处理POW共识,另外一个处理PBFT共识。所以,是智能合约能够选择?还是有POW的部分以及PBFT的部分?如果根据需要来选,只是用一个共识机制的话,是不是又违背了混合共识将两种共识有点结合的初衷?*

> 与以太坊不同的是,交易和智能合约由网络上的每个节点执行,轮值BFT委员会进行大量处理的能力将会有所提升,而PoW(慢链)只用于选择委员会成员。在极限情况下,我们将委员会轮转频率到一个块和cSize=1,我们恢复传统的POW共识。

*[注] 这里的每个节点是指POW节点吗?BFT委员会的处理能力是用来做什么?是用来进行分块的重新排序和设定吗?*

> 单纯的使用Nakoto链作为慢链,很容易就能明显的感受到自私挖矿攻击策略的影响。如果一个自私的矿工控制了超过25%的区块链中的算力,他可以控制33%以上区块的产生。

*[注] 这里的数字事如何产生的?为什么25%的算力会能控制33%的区块*

> 我们提出的解决方案如下。当一个诚实的BFT节点达到λ链的长度,将发布链中的每个果实链的唯一矿工ID作为候选人(或者,每个矿工ID开采超过ν水果)。通过VRF的应用,从候选人中随机选出新的BFT委员会。
显然,Sybil攻击在这个方案下是不可能的,因为你需要最低等级的PoW才能成为候选人。对于一个庞大的矿池来说,要在拜占庭委员会达成妥协也并非易事。一个水果比一个区块更容易挖掘。

*[注] 感觉使用水果而非算力或VRF,水果的门槛比VRF高,防止女巫攻击。而水果的门槛又比算力的门槛低,从而增加候选人,再进行随机选举。从而避免大算力的垄断。*

## 挖矿流程

> 矿工只能运行一个挖矿算法随机产生散列值h。当[h]-κ<Dpf时开采果实,并且当[h]κ<Dp时开采块,其中Dpf和Dp分别是果实和块的挖矿难度参数。元组(R,Dp,Dpf)用来决定挖矿过程。

*[注] 一个矿工是同时可以挖区块和水果,还是只能单独挖?从后面的算法可以看到,一个矿工应该是既可以挖水果又可以挖区块,主要取决于难度。*

Algorithm 4: Blockchain growth process
算法4:区块链增长过程
1 Initialize
// chain是区块的集合
2 chain = chain[0]
3 chain[0] = (0; 0; 0; 0; ⊥; H(0; 0; 0; 0; ⊥), null)
// F是水果的集合
4 F = null
5 if heard fruit f ′ then
6 if f ′ is the unique fruit corresponding to message m′ then
// 如果f'根据m'的信息看是独一无二的
7 F = F ∪ f ′
8 if f ′.h-1 < f .h-1 for all other fruits f such that f .m = m′. then
// 如果对于其他的区块,f'.h-1 < f.h-1从而实现f.m = m'
// 这里的h市值对应的难度还是指编号?f与f'又是什么区别?
// 还是指f'是其他矿工挖出的水果,这里进行比较,来查看目前挖掘的水果是否还有效
9 F = F ∪ f ′
10 if heard blockchain chain′ and |chain′.F | > |chain.F | then
11 chain = chain′
12 where |chain.F | is the total number of fruit contained in chain.
// |chain.F |是指区块中包含水果的数量
13 foreach time step (1 sec) do
14 Heard signed message m, broadcasted by PBFT. Let
// 监听所有签名的消息m,通过PBFT来广播
15 l = |chain| - 1, so chain = (chain[0], ..., chain[l]).
16 F ′ = {f ∈ F : f recent w.r.t. chain, f !∈ chain}
17 h′ = chain[pos].h-1 where pos = max(1, l - κ).
18 h-1 = chain[l - 1].h.
19 while mined = FALSE do
20 Randomly pick η ∈ {0, 1}κ
//进行运算,类似比特币的哈希碰撞
21 Compute h = H(h-1; h′; η; d(F ′); m)
//如果满足水果的难度,则进行挖掘水果
22 if [h]-κ: < Dpf then
23 f = (h-1; h′; η; d(F ′); m, h)
24 F = F ∪ f
25 boardcast fruit f
26 mined = FRUIT
// 如果满足区块的难度,则进行挖掘区块
27 if [h]:κ < Dp then
28 chain[l] = ((h-1; h′; η; d(F ′); m, h), F ′)
29 broadcast blockchain chain
30 mined = BLOCK

*[注] 矿工同时监听水果和区块,如果有最新的区块或难度系数更小的水果,则收纳该水果和区块,进行新的挖矿。如果没有,则进行挖矿。如果挖掘出的h满足水果的难度,则收纳该水果并进行广播。如果满足区块的难度,则收纳该区块并进行广播。*

## 存储挖矿

> 其中每个节点只需要存储来自前n个检查点的交易历史记录。但是我们应该把交易历史的其余部分存储在哪里呢,因为没有人有动机去存储它。我们的解决方案是在系统的激励基础设施中无缝地将交易处理与IPFS的存储能力合并。
初链上的数据存储可以分为三个层次:
层次1:存储在每个PoW节点上,例如比特币和以太坊。 这是最永久的存储方式,也是效率最低的方式。不建议存储短文本以外的任何内容,例如关键信息的hash值。用户向PoW矿工支付gas费。
层次2:将会有一个类似ipfs的文件系统,其中数据的有限副本分布到整个链中的存储节点。这是为存储主网络的大量历史交易和大量数据的去中心化应用程序而设计的。用户将向矿商支付存储和检索的费用。
层次3:本地存储。该文件将只存储在您自己的本地节点。这对于点对点通信很有用,例如聊天程序,用户更关注没有人窃听。这是免费的。

*[注] IPFS对应的激励机制filecoin还没有上线。这里的存储和检索费用是用初链自己开发的代币还是直接使用filecoin?有了大量的存储空间的基础设施,一些应用就更容易进行部署。*