VRF 这个东东,在不少区块链项目中都会听到,比较火的 Algorand 和 Dfinity 的共识机制都用到了它。它的全称叫 Verifiable Random Function(随机可验证函数),那它跟一般的随机函数有什么不同?有什么用,为什么区块链需要用到 VRF?这次被我骚扰的是资深全栈工程师黄祺,他曾在 BFTF 区块链知享会中介绍区块链中 VRF 的应用。
在区块链中,大部分的共识算法,无论是 POW、POS,或是由他们衍生出来的 DPOS,都需要选出一堆或者一个节点来参与共识或者打包区块,这个过程虽然会有持币情况、设备配置、信誉等各种因素影响,但必须是随机的、无法被预测的。这时候就可能会用到随机算法。
在 BFTF 的分享沙龙上,黄祺解释,可验证随机函数可以看作是一个随机预言机(Random Oracle,RO),就是可以通过任意的一个输入,获得一个随机数输出。可验证随机函数比随机预言机多了一个非交互的零知识证明,可以用来该随机数输出的正确性,表明这个随机数的确是某个人生成的。
先说 RO,有两个特征:
1、对于不同的 Input,Output 的值是随机的,并且均匀分布在值域范围内。
2、对于相同的 Input,它得到的 Output 一定是相同的。
举例说明一下,假设某公链网络用普通的 RO 选节点,有可能是这样的情况:假设全网有 100 个节点,我想生成下一轮一个节点谁打包,我以某一轮的轮次作为输入,然后随机输出的值必须要是在 1-100 之间的自然数(因为网络中只有 100 个节点)。这就每一轮都选出了一个打包节点的人。
这里的问题是,由于输入对应的输出肯定是相同的,而输入是公开的,就使得每一轮的抽签结果变得可以被预知,攻击者可以尝试控制这个过程或者攻击特定的节点。可是如果输入不公开的话,我们要怎么保证这个输入结果没有问题呢?VRF 就用到了零知识证明,让结果“可验证”。
VRF 的方式是,让各个节点自己抽签,如果抽中了之后,大家可以很容易地验证这个结果确实是你生成的。具体过程有可能是这样的:假设现在是 round 10(第 10 轮),节点们可能会轮流抽签,以节点自己的私钥 + 一个全网都知道的随机数(比如是这轮的轮次 10)作为输入,生成了一个随机数(0-100);设置一个条件:100 个节点轮流抽签,谁先抽出来的随机数大于 10,就是这一轮的打包者。假设 5 号节点抽到了 11,可是只有 5 号知道其他人不知道,因此他在广播这个随机的同时还需要广播一个零知识证明。通过零知识证明,全网只需要通过 5 号的公钥就可以验证,接受 5 号为这轮打包者。
So,普通 RO 在私钥 + 零知识证明的作用下,抽签过程就可以在本地进行、不公开私钥同时又可以全网验证。
黄祺总结,可验证随机函数一共包含四个函数:1、生成密钥,生成一个公钥私钥对;2、生成随机数输出;3、计算零知识证明;4、验证随机数输出。
看完上面的之后,我们大概可以明白,VRF 的目的就是要生成一个真正随机而且无法被预测的值。在区块链选出块节点的过程中,为了保证安全,随机是一个基本要求。不过,区块链选节点不单纯是随机就 OK 的,还要考虑到攻击成本等,所以共识机制往往加入算力和持币权益等影响因素,以增加攻击者的攻击成本。如果单纯使用随机算法,就很容易受到女巫攻击,攻击者可以廉价找大量的傀儡机(肉鸡)来增加自己抽中的概率。
这么一想,你会很容易明白为什么有那么多新型的共识算法都用到了 VRF,其实它们想达到的目的跟 POW 的答题过程有点像,都是为了找到随机而又安全地抽取出块节点。POW 被诟病的问题是功耗大和性能低,但是安全边界明显,而且比特币运行已久都没有大问题。POS 共识算法本身不需要大量算力,VRF 可又以在本地抽签,所以 POS 共识算法用 VRF 的好处是功耗比较低,而且最新的算法,验证零知识证明的速度已经非常快。有不少知名的公链项目都用到了 VRF,包括本体、Cardano (共识算法为 Ouroboros,已经迭代了 uroboros、Praos 和 Genesis 三个版本)、Dfinity 和 Algorand。
不同的项目用到 VRF 的不同点主要在于是怎么产生一开始的输入,以及输出要怎么用。以下是 VRF 在一些项目中的作用:
根据本体官网,VBFT 的每轮共识中:
根据 VRF 从共识网络中选择备选提案节点,各个备选节点将独立提出备选区块;
根据 VRF 从共识网络中选择多个验证节点,每个验证节点将从网络中收集备选的区块,进行验证,然后对最高优先级的备选区块进行投票;
根据 VRF 从共识网络中选择多个确认节点,对上述验证节点的投票结果进行统计验证,并确定出最终的共识结果。
所有节点都将接收确认节点的共识结果,并在一轮共识确认后开启新的共识。
根据上交所技术公司的朱立解释,Algorand 和 Dfinity 的共识算法中用 VRF 的套路大概是这样的:输入值由前一个随机数(最初的随机数却是协议给定的)和某种代表高度、轮次的变量进行组合,然后用私钥对之进行签名(或者是先签名再组合),最后哈希一下得出最新的随机数。他认为:
“这样产生的随机数旁人很容易验证其合乎算法,"V" 就这样得到了;而哈希返回值又是随机分布的,“R” 也因此得到保证。在此过程中,为降低操纵结果的可能性,有两个注意事项:A) 签名算法应当具有唯一性,也就是用同一把私钥对同样的信息进行签名,只有一个合法签名可以通过验证——普通的非对称加解密算法一般不具备这个属性,如 SM2。如果用的签名算法没有这种 uniqueness 属性,那在生成新随机数的时候就存在通过反复多次尝试签名以挑出最有利者的余地,会降低安全性。B) 避免在生成新随机数时将当前块的数据作为随机性来源之一,比如引用本块交易列表的 merkle root 值等等,因为这样做会给出块人尝试变更打包交易顺序、尝试打包不同交易以产生最有利的新随机数的余地。在设计和检视新的共识算法时,以上两个注意事项是要特别留意的。
VRF 的返回结果可以用来公开完成节点或节点群体的选择,也可以私密地完成选择。以 Dfinity 为例,它是利用 mod 操作来唯一、公开地确定一个 Group。Algorand、Ouroboros Praos 是私密选择的范例,大致套路是对 VRF 的最新返回值,配上轮次等变量后用私钥进行签名并哈希,如果哈希值小于某个阈值,节点就可以私密地知道自己被选中。这种方法很可能在网络节点数较多时的表现会更稳定,否则幸运儿个数上下波动会较大,进而影响协议表现,包括空块和分叉。”
根据 CSDN 博主 Omni-Space 解释,Cardano 的共识机制运作如下:
“首先是一些术语及作用的解释:
在 Cardano 的运行中,时间被分为 slot。
每个 slot 只能产生一个块,若这个块有问题,或者应该产出这个块的“矿工”(也就是 stakeholder 的候选人)不在线,或者产出的块没有广播给大多数人,那么这个 slot 是当作废弃的,也就是会跳过这个 slot 的块。
图片来自黄祺《区块链中 VRF 的应用及原理解析》
多个 slot 为一个 epoch,权益的计算是以每个 epoch 开始前的历史来计算,也就是说在这个 epoch 中所产生的权益变化不影响当前的这个 epoch 中的 slot 的出块者的选择和其他和历史相关的东西。当前 epoch 中所产生的这些历史只能在以后的 epoch 中生效。
每个 epoch 的开头有个 genesis block(注意是每一个 epoch,不是整个链),这个 genesis block 不会上链 (整个链初始的那个 genesis 会,当然这一点可以根据实现自己决定),而是当前这个节点(矿工) 自己在内存中生成,这个 genesis block 会记录好当前这个 epoch 中的可能参与出块的 stakeholder 的候选人,及一个随机种子ρ。
stakeholder 是权益持有者,也就是潜在矿工,在 Cardano 的实现中权益 stake 并不是直接指代有多少 Ada,而是和有多少 Ada 相关联(更详细的我不是很清楚),同时要成为一个 stakeholder 需要有 2% 的 Ada 才行。当然论文中不关注这些,直接定义了 stakeholder。而 stakeholder 并不一定要参与出块,只有记录在每个 epoch 的 genesis block 中的 stakeholder 才能参与当前 epoch 中 slot 的出块,所以记录在每个 epoch 中的 genesis block 中的 stakeholder 叫做 “stakeholder 候选人”
由这些 epoch 衔接而成的链就是由 Ouroboros 共识产生的链,这个链的基本属性和 Bitcoin 相同(如每个块包含上一个块的 hash 等等)。
Slot Leader Selection 是指根据权益占比选择按概率选择出当前 slot 的出块者。指代的是在当前的 epoch 中,按 genesis block 中记录的 stakeholder 候选人的权益分别占用的比例为这个 epoch 中的每一个 slot 选择出块者的概率(注意这个概率是独立事件,也就是有可能在同一个 epoch 中重复选出相同的人)。在论文中用函数
来表示按照权益占比为概率从stakeholder候选人选出该slot的出块者U。注意在论文中只是定义了这个函数具有这样的作用,在Cardano中使用了 “follow-the-satoshi(fts)” 算法(fts具有论文中定义的这个函数的性质)来选出这个slot leader也就是出块者。
secure multiparty computation (MPC) MPC协议,参与者使用一种能达成MPC的密码学协议来参与生成下一轮epoch的随机种子ρ,这个MPC协议必须是保证guarantee output delivery(G.O.D,下文会解释)。这个随机种子ρ是用于Slot Leader Selection中的。
在已有这些基础术语及作用的基础上,现在来简单介绍一下的工作流程:
如图所示,执行流程如下:(注:我并不保证这个流程是完全符合Cardano源码的实现(毕竟我没看过),但是是符合论文的描述的):
从链的真正创世块开始,硬编码进入了一些公钥和这些公钥vk对应的权益s及初始的种子ρ,之后,这个epoch会采用这些基础信息继续运行。
每个节点自己独立运行代码,根据当前epoch的种子ρ,执行F(比如 follow-the-satoshi),把genesisblock中的权益,ρ和slot的index作为输入,根据概率获得当前这个slot应该由谁出块。
若发现是自己出块,则执行打包交易等等操作,和bitcoin没有太大区别,但是除了基础工作之外,还会生成一个随机数,但是这个随机数不放到链(块)中,而是放一个承诺(后文解释)。
若不是自己出块,则等待出块者出块并广播。收到这个块的时候就进行和bitcoin类似的检查,要是长时间未收到(超出这个slot的时间)则会认为这个slot的块废弃。
在当前epoch中不断重复2的流程直到这个epoch中的所有slot结束。
在整个epoch的过程中会产出一个在这个epoch参与出块者们(slot leaders)都共同认同的随机种子ρ。
在自己的内存里记录好这个ρ及下一个epoch参与的stakeholders,开启下一个epoch周期,进入2的流程。
以上就是 Ouroboros 大致执行的流程。”
VRF在区块链领域的运用大致如上,不过在如上场景有没有更好的解决方式,或者这个技术还能用在什么场景,还是值得讨论的问题。
参考文章:
随机选择算法
区块链中VRF的应用及原理解析
对可验证随机函数VRF的简明解释
简评三个基于VRF的共识算法
可验证随机函数VRF之Algorand算法
图灵奖得主Sivio Micali的Algorand区块链协议简介
Algorand 白皮书 1607.01341 版本
Cardano白皮书
Verifiable Random Functions