作者:Vitalik Buterin
译者&来源:LinktimeTech 微信公众号
在刚刚闭幕的以太坊Devcon 2大会上,以太坊创始人Vitalik分享了他的最新研究成果《以太坊紫皮书》,在此我们将其翻译成中文,供大家阅读。《紫皮书英文》原版大家也可以从我们的公众号中找到并阅读。当然,如此前沿和复杂的论文翻译是很大的考验,如果大家觉得以下内容有问题,请及时与我们反馈,我们将在后期的公众号文章中进行纠正。论文全文内容如下:
(为什么叫紫皮书?)
前言
在过去十年中,诸如Bitcoin、Namecoin和以太坊这些项目充分展现了密码经济共识网络对促成下一代去中心化系统的强大推力,并潜移默化的将开发视野从简单的数据存储和信息服务扩展到任何状态应用的后台管理。基于这个系统,在全球范围内提议和执行的应用,涵盖了全球低价支付系统,金融合同,市场预测,身份注册和现实世界产权,建立更安全的证书管理系统,甚至通过供应链对制造品进行溯源和跟踪管理。
但是,这个系统的技术基础仍然存在严重的效率问题。因为在网络上每个全节点都必须维护整个系统的状态和处理每个交易,整个区块网络效率受限于单个计算节点。现在大部分系统所采用的共识机制,工作量证明(POW),需要消耗大量的电力去运营; 基于POW机制的最大的工作区块链Bitcoin,消耗了相当于整个爱尔兰的用电量。
这篇文章为上述问题提供将POS和基于分片证明进行合并的解决方案。POS本身并不是一个新奇的主意,2011年就已经存在,但新的算法展现了实质性的好处,不仅解决了前一系统的缺陷,甚至拥有POW不曾有的属性。
POS可以被想象成一种虚拟挖矿。然而在POW模式下,用户需要花费一定的金钱买一台电脑,然后消耗真实的电力,其随机获得区块链的成本与消耗的电力大致成比例。在POS模式下,用户花费金钱购买系统内的虚拟代币,然后用一个内部协议机制将虚拟代币转换成虚拟电脑,系统模拟随机产生的区块的成本与购买成本大致成比例,达到了POW同样的出块效果,却不用消耗电力。
分片也并不新颖,在现行的分布式数据库设计中有超过10年的应用,但是到目前为止,研究将其应用在区块链上,仍有颇多限制。基本的路径是解决可扩展的挑战, 通过架构中的全球验证程序集合中的节点(在我们的情况下,通过股权结合证明了)被随机分配到特定的“碎片”,其中每个碎片并行处理全局状态的不同部分,从而确保工作是跨节点分布处理,而不是每个节点都重复做。
我们渴望实现以下目标
我们从描述一个算法开始,该算法实现目标1和2, 然后在第二个算法实现了目标3, 然后在第三个算法中一定程度上实现目标4,5(作为一个限制条件,对一个节点的计算能力的平方大致成比例,如(4)和一个24小时的延迟跨碎片信息,有可能建立更快的消息作为一个层上通过双用途的存款,在例(5)。对目标(4)和(5)的更强级别的满意,(6)也一样,使得重新设计2.1和3.0。
常数
我们设置:
最小的POS
(注意,后续内容假设读者有对以太坊1.0的基本了解)
我们可以创建一个最小可行的PoS算法,没有诸如敲定确定、额外的抗审查以及分片等特性。在地址CASPER_ADDRESS存在一个合约,其主要功能是追踪验证者集合的变化。该合约没有特殊的特权,除了调用该合约是验证区块标头过程中的一部分,而且是包括在创世区块,而不是通过交易被动态添加的。验证者集合初始设置在创世区块中,并可以通过以下函数进行调整:
接受一定量的以太币作为保证金,发送者指定一段验证代码(本质为EVM字节码,代码主要功能是用作一种公钥,以便后续其他的节点验证由他们进行签名的区块数据以及相关的网络共识消息),一个随机提交的交易(一个32字节的哈希用于验证者的选择;详见下文)和最终提现地址一并发送。值得注意的是,提现可以在一个特定的地址发送,该地址的合约功能唯一用途就是特定的地址条件下释放资金,如果需要还可以双重用保证金。如果所有参数都被接受,在下一个时期将增加该验证者到验证者集合。(例如,如果保证金在第N个时期请求提取, 而验证者在第N+2个时期被加入验证者集合,这个一个时期等于EPOCH_LENGTH个区块时间周期)。验证代码的哈希值(叫做vchash)可以被验证者用作识别号;不同验证者具有同样的哈希值是被禁止的。)
startWithdrawal(bytes32 vchash, bytes sig):
开始撤回流程,要求一个签名,该签名需要通过验证者的验证代码。如果签名通过,从下一个时期开始,验证者被从验证者集撤回。注意,这个函数不退回以太币。
还有一个函数:
withdraw(bytes32 vchash):
撤回验证者的以太币到指定的提现地址,增加奖励减少惩罚,只要验证者已经从验证者活动集合中通过使用 StartWithdrawal至少是WITHDRAWAL_DELAY几秒前撤回。
准确的讲,验证代码就像是放入区块标头的哈希码,加上签名,如果签名有效返回1,反之,则返回0。这个机制确保了我们不会将验证者锁定到任何一个特殊签名的算法,相反允许验证者使用验证代码从多重私钥验证签名取代单一验证,也允许使用Lamport签名对抗量子计算机的攻击。这个代码在黑匣子环境下通过使用新的CALL_BLACKBOX操作码执行,以保证执行独立于外部状态。这样可以防止一些攻击,如一个验证者创建了验证代码在状态良好的时候返回1,在状况不好的时候(例如 Dunkle inclusion)返回0.
deposit函数中randao 参数的值应该是计算一段长链哈希值结果,即设置秘密随机的X,执行计算randao = sha3(sha3(sha3(sha3(.....(sha3(x))…))。每个验证者提供randao的值保存在Casper 合约中的存储空间。
Casper合约也含有一个叫GlobalRandao的变量,初始化为0。这个合约含有一个函数getValidator(uint256 skips):返回 skips 跳过以后的验证者的代码。例如 getValidator(0)返回第一个验证者(验证者一般来讲可以创造区块),getValidator(1)返回第二个验证者(如果第一个不能创建区块,验证者可以创建)
每个验证者都是通过伪随机算法从目前活动的验证者集合进行选择,随机对初始保证金规模进行加权,并以Casper 合约中的globalRandao值为伪随机种子。除了签名外,一个有效的区块也必须包含为那个验证者目前保存Randao的原像。这个原像然后替换保存Randao值,然后也通过异或运算保存到合约的globalRandao值中。这样,一个验证者生成的每一区块都要求脱掉一个验证者的randao的一层。这是一种基于在这里解释( http://vitalik.ca/files/randomness.html )的随机算法。
总之,一个区块必须包含的如下的额外数据:
<vchash><randao><sig>
其中,vchash是验证代码的32字节哈希值,用于快速识别验证者。randao含义如上所描述(也是32字节),sig是签名,可以是任意长度(虽然我们将区块标头的大小限制为2048字节)。
创建区块所需要的最短时间可以简单的定义为: GENESIS_TIME + BLOCK_TIME * <block height> + SKIP_TIME * <自创世区块后所有的验证者跳数和>。
在实际过程中,这意味着,一旦发布了某个区块,那么下一个区块的0-skip验证者会在BLOCK_TIME秒之后发布,同理,1-skip验证者则在BLOCK_TIME + SKIP_TIME秒之后发布,以此类推。
如果一个验证者发布一个区块太早,其他的验证者会忽视该区块,直到在规定的时间之后,才会处理该区块(该中机制的进一步描述和验证详见这里( http://vitalik.ca/files/timing.html);短BLOCK_TIME和长SKIP_TIME之间的不对称性,可以确保:在正常情况下,区块的平均保留时间可以非常短,而在网络延迟更长的情况下,也可以保证网络的安全性)。
如果一个验证者创建了一个包括在链内的区块,他们得到的区块奖励相当于此周期内活动验证者集合内以太的总量乘以REWARD_COEFFICIENT * BLOCK_TIME。因此,如果验证者总是正确的履行职责,REWARD_COEFFICIENT本质上成为验证者的“预期每秒收益率”;乘以~3200万得到近似的年化收益率。如果一个验证者创建了一个不包括在链内的区块,之后,在将来的任意时间(直到验证者调用withdraw函数为止)该区块标头可以作为一个“dunkle”通过Casper合约的includeDunkle函数,被包括在链内;这使得验证者损失了相当于区块奖励的钱数(以及向包括dunkle在内的当事方提供一小部分罚款作为激励)。因此,验证者应当在确定该区块在链内的可能性超过50%,才实际创建该区块;该机制不鼓励所有链上的验证。验证者的累计保证金,包括奖励和罚款,存储在Casper合约的状态内。
“dunkle”机制的目的是为了解决权益证明中的“零赌注”的问题,其中,如果没有罚款,只有奖励,那么验证者将被物质激励,试图在每个可能的链上创建区块。在工作量证明场景下,创建区块需要成本,并且只有在“主链”上创建区块才有利可图。Dunkle机制试图复制工作量证明中的经济理论,针对创建非主链上区块进行人工罚款,来代替工作量证明中电费用的“自然罚款”。
假设一个固定大小的验证者集合,我们可以很容易的定义分叉选择规则:计算区块数,最长链胜出。假设验证者集合可以变大和缩小,但是,该规则就不太适用了,因为作为少数支持的分叉的速度一个时期以后将像多数支持的分叉一样。因此,我们可以用计算的区块数代替定义的分叉选择规则,给每个区块一个相当于区块奖励的权重。因为区块奖励与积极验证的以太总量成正比,这确保了更积极验证以太的链得分增长速度更快。
我们可以看出,这条规则可以用另一种方式很方便的理解:基于价值损失的分叉选择模型。该原则是:我们选择验证者赌最多价值的链,就是说,验证者承认除了上述的链外,所有其他链都会损失大量的资金。我们可以等同认为,这条链是验证者失去资金最少的链。在这样一个简单的模式中,很容易看出这如何简单的对应着区块权重为区块奖励的最长链。该算法是尽管简单,但用于股权证明的实现来说也足够高效。
添加敲定
下一步是增加经济敲定的概念。我们按照以下方式进行。在区块标头内部,除了指向上一个区块的哈希之外,现在验证者也对某个以前区块FINALIZATION_TARGET敲定概率做出了一个断言。该断言被当做一个赌注,如“我相信区块0x5e81d……将被敲定,并且在所有区块史中,如果这一断言是错误的,我愿意损失V_LOSS,假设在所有区块史中,这一断言是正确的,我将得到V_GAIN。”验证者选择odds参数,而V_LOSS和V_GAIN,按照如下方式计算(令total_validating_ether为活动验证者集合的以太总量,MAX_REWARD是允许的区块奖励的最大值,bet_coeff是下文定义的系数):
V_GAIN and V_LOSS values relative to the BASE_REWARD, assuming bet_coeff = 1,假设bet_coeff = 1,V_GAIN值和V_LOSS值与BASE_REWARD的相关性
FINALIZATION_TARGET从null开始, 但是区块1期间,它被设置成区块0,。bet_coeff初始(开始)值被设置为1,另一个变量cached_bet_coeff设置为0。但是bet_coeff不能减少到低于MIN_BET_COEFF,在每个区块,我们设置bet_coeff -= bet_coeff / FINALITY_REWARD_DECAY_FACTOR,cached_bet_coeff -= cached_bet_coeff / FINALITY_REWARD_DECAY_FACTOR(这确保了总有激励驱动去打赌)。当创建一个区块,验证者得到BASE_REWARD * log(MAXODDS) * cached_bet_coeff,其中MAXODDS是最大的可能赔率,如MAX_DEPOSIT_SIZE / BASE_REWARD。该机制随所实现的是,一旦某一区块被确定被敲定,验证者从中得到奖励,就像他们以最大odds继续进行验证一样。这确保了,验证者不会受到不正当的激励而共谋通过延迟敲定某个区块以获得最大收益。
当Casper合约确定FINALIZATION_TARGET已被敲定(即已知区块的总价值损失超过某个阈值),我们将新的FINALIZATION_TARGET设置成当前区块,我们设置cached_bet_coeff += bet_coeff,并且bet_coeff重新设置为1。从下一个区块开始,敲定过程重新开始设置新的FINALIZATION_TARGET。如果,有一个短链分裂,敲定过程可能同时处理多个区块,甚至是不同高度的区块;但是,鉴于区块投注的默认验证者策略,以及支持它的最高价值损失,我们预计收敛过程倾向于选择他们其中的一个(此处的收敛参数基本上与最小权益证明相一致)。
当新的区块敲定过程开始启动时,我们期望最初的赔率很低,这表示验证者对短范围分叉的恐惧,但是随着时间的推移,验证者愿意投注的赔率会逐渐增加。特别的如果他们看到其他验证者对该区块进行高赔率投注,验证者的赌注也会增加。可以预计,该区块的价值损失将以指数倍增加,从而在对数时间内,达到最大“总保证金损失”。
在区块标头的额外数据中,我们现在将所需的数据格式更改为以下格式:
<vchash><randao><blockhash><logodds><sig>
其中区块哈希是上个打赌的区块的哈希,并且logodds为1字节值,代表对数形式的赔率(即,0对应1,8对应2,16对应4等)。
请注意,我们不能允许验证者完全自由地设置赔率。考虑如下场景,如果有两个竞争关系的敲定目标,B1和B2(即存在两个链,其中一个的FINALIZATION_TARGET设置为B1,另一个的的FINALIZATION_TARGET设置为B2),并且围绕B1开始形成共识,然后一个恶意验证者可能会突然对B2投入一个高赔率赌注,它的价值损失足够影响共识,从而引发短范围分叉。因此,我们使用以下规则,通过限制V_LOSS来限制赔率:
该规则设计上引入了一个安全约束:当至少(样本)三分之二的其它验证者风险为x后,当前验证者的风险仅可以为1.5x。这与拜占庭容错共识算法的事先承诺或承诺模式类似,一个验证者应等待其他三分之二的验证者完成给定的步骤后,才能进行下一步,并确保一定的安全,也确保大多数共谋不能参与“恶意破坏”攻击(如,让其他的验证者讲大量赌注添加在一个区块,并且随后推动共识到一个别的区块),无法共谋,因为共谋本身需要耗费大量资金(实际上,共谋损失金钱的速度比受害者损失金钱的速度更快,这是一个显著的特性,因为它确保,在大多数敌对情况下,恶意操作者会随着时间的推移,经常会被“淘汰”。)
如果一个区块作为一个dunkle被加入链中,赌注被处理,并且罚款和奖励也会产生。例如,如果有两个高度为5000的区块A1和A2,互为竞争的敲定目标,并且有两个高度为5050的区块B1和B2(两区块均以A1为上一代区块),并且验证者在B1上构建区块C,对A1下赌注,随后,如果B2最终确定为主链区块,B1和C将成为dunkle,并且C也将因B1 vs B2下错注而被惩罚,但是还是会因A1被赌对而进行奖励。
然而,假设C中的V_LOSS是这样的,如果B1包含在内,则V_LOSS < V_LOSS_MAX;如果B2包含在内,则V_LOSS > V_LOSS_MAX。之后,为了保证预期的价值损失特性,我们制定了一个额外的罚款:即使验证者赌对了,但我们仍以V_LOSS - V_LOSS_MAX对他们进行处罚。因此,我们有效地将V_LOSS赌注,分解成(i)价值损失V_LOSS_MAX的赌注和(ii)V_LOSS - V_LOSS_MAX的单纯价值销毁,从而保证这种规模过大的赌注仍仅通过V_LOSS_MAX经由分叉选择规则变换。这意味着投注在某种意义上是不“完全的”,因为如果该区块的子代区块中的很多都分叉了,即使该区块已经完成敲定确定,某个区块的赌注仍可能引起罚款。在投注模型中的单纯价值损失,被认为是对价值损失分叉选择规则利润纯度的一个可接受的权衡。
计分与策略实现
价值损失计分可以通过以下算法实现:
此处用于举例的V_LOSS;在现实中,他们不会被允许这样快速的增长,并且B或C会要求A上的较高的V_LOSS成为敲定候选区块。
一个简单的验证者策略是仅在头部创建区块,并且做出敲定赌注,此处的价值损失是所描述的最大价值损失的80%。
轻客户端同步
敲定机制开启了一个快速轻客户端同步算法的大门。该算法包括以下几步:
非常值得关注的是,通过步骤1到5可以用两个网络请求和几秒钟计算来验证全天的区块。
分片
现在,我们考虑从一个分片扩大到多个分片。我们构建的模型如下。取代已有的单条区块链,我们现在拥有被我们称为“分片”的多条相关联的区块链。NUM_SHARDS分片的编号为分片0至分片NUM_SHARDS – 1,其中分片0简单的作为常规股权证明区块链进行,其确定性如上所述,但是分片1……NUM_SHARDS – 1工作机制有所不同。在每个时期的开始,为每个分片随机挑选VALIDATORS_PER_SHARD个验证者,并且被分配为下一个时期的验证者(如,n+1时期的验证者在n时期时被指配)。当调用getValidator(skip)以确定这些分片内一个分片的验证者,仅仅随机从选取的验证者集合中选择一个验证者(平等地分配,因为在挑选的时候就已经对保证金大小进行加权)。分片1……NUM_SHARDS – 1的确定性赌注并未在分片内做出,而是在分片0内做出。当赌注被做出,它就被存储,赌注仅在子代区块周期结束后,才被处理(如,n+1时期的区块确定断言,将在n+3区块周期开始时,在分片0内进行处理)。
如果已经为一个分片挑选了一个验证者,那么这个验证者将需要调用Casper合约的registerForShard(bytes32 vchash,uint256 shard,uint256 index,bytes32 randao)函数,其中vchash是验证者的验证代码哈希,shard是分片ID,index是一个数值且0 <= index < VALIDATORS_PER_SHARD,其中得getShardValidator(uint256 shard,uint256 index)返回给定的验证代码哈希,并且randao是一个randao承诺。为了引导验证者,该函数生成了一个收据,该收据能够通过利用confirmReceipt(uint256 receiptId)在目标分片上进行确认。
getShardValidator依赖于一个单独的随机源,业务逻辑与getValidator类似。该随机源,按照如下步骤得出:
使用Iddo Bentov的低影响函数,增加了随机源的操纵成本,因为,这个特别的随机源种子会产生实质性的经济结果,因此,它比正常操纵目标更大一些。
跨分片敲定赌注并不在区块标头上,所以不会过度的阻碍轻客户端;相反,验证者将创建一个交易,在它们创建的任何区块间,调用一个registerFinalityBets(bytes32[] hashes,bytes logodds)函数,任意一个区块中创建预期的NUM_SHARDS个哈希和一个长度为NUM_SHARDS的字节数组,每个字节代表相应的区块哈希的赔率。
验证者的典型工作流程是维持分片0的一个“全节点”,并且保持追踪被分配给它们的未来分片。如果一个验证者被分配给一个分片,他们将利用Merkle树证明下载状态,并且确保当他们需要开始验证时,他们已经下载了相应状态。对于该时期,他们作为该分片的验证者,并且创建区块,同时,他们将在所有的分片上做出敲定赌注,他们通过观测(i)每个分片上的最长链,(ii)其他的验证者的敲定赌注,以及(iii)在片区内试图达到51%成功攻击的各种二次启发方法和机制(如,欺诈证明)。注意,分配到任何给定分片的概率与验证者的累计以太成比例,因此,如果验证者的资产成倍,那么需要处理的计算也会加倍。这个特性被认为是何意的,因为它增加了公平性,并降低了矿池激励,同时引入了一个原则,该原则下处理交易并存储区块链本身也成为了“工作量混合证明”的一种形式。
取样机制的初衷是为了确保系统仅依靠少量验证者进行实际交易验证,同时系统能够安全应对累积以太保证金高达~33-40%(低于50%,因为总累积以太为33-50%的攻击者在某些给定的分片会很“幸运”)的攻击者;因为取样是随机的,攻击者不能够选择将他们的权益集中在一个分片上,许多工作量分片方案的一个致命缺陷上。即使一个分片被攻击了,还有第二道防线:如果其他验证者发现攻击的证据,那么他们可以拒绝做出跟随攻击者分叉的敲定声明,而确认由诚实节点创建的链。如果一个分片上的一个攻击者试图从无效区块创建一个链,其他分片的验证者可以检测到,并且之后暂时完全验证该分片上的节点,并且确保他们仅敲定有效的区块。
跨分片通信
本方案中的跨分片通信遵循如下工作。我们创建一个ETHLOG操作码(带有两个参数:to value),其创建的一个日志记录,其存储内容是空字符串(注意,该空字符串,不是32个零字节,一个传统的LOG只能存储32字节的字符串),它的数据是一个64字节的字符串,包括目标和具体值。我们创建的GETLOG操作码,需要一个由区块定义ID的单独的参数。number * 2**64 + txindex * 2**32 + logindex(其中txindex是交易的index,包括区块的log,logindex是交易receipt中的log index)试图获取指定的存储内容,存储在状态中的日志表明,该日志已被消耗,并且将日志数据放在目标数组内。如果日志值为空串,这也将传输以太给收件人。为了成功得到日志内容,调用操作码的交易必须携带日志ID参数。如果v=0,我们允许签名中的r值重新用于此目的(注意:这意味着此处仅可以使用EIP 86交易;我们希望到现在为止,EIP 86交易将是交易的主要形式)。
现在抽象共识再也不是一个单链,而是一个链的集合,c[0]…c[NUM_SHARDS - 1]。状态转移函数不再是stf(state, block) -> state',而是stf(state_k, block, r_c[0]…r_c[NUM_SHARDS - 1]) ->state_k,其中
复制代码
是来自过去的超过ASYNC_DELAY区块的i链的receipt集合。
请注意,有几种方法可以“满足”这种抽象。一种方法是“每个节点都是全功能节点”的方法:所有节点存储所有分片的状态,更新所有分片的链,因此拥有足够的信息,可以计算所有的通道交易函数。然而,这是令人乏味的,因为它是不可扩展的。
更有趣的策略是“中型节点”方法:大多数节点选择一些分片,保持更新到最新(尽可能包括分片0),并作为所有其他分片的轻客户端。当计算通道交易函数时,他们需要旧交易收据,然而,他们并没有存储旧交易收据,为此,我们增加了一个网络协议规则,要求交易伴随着任何交易静态参考的收据的Merkle证明(这里为什么需要静态参考就很清楚了:否则,在运行时间创建的任意一个GETLOG操作,因为网络延迟,日志读取数据将变成一个缓慢的过程,将耗费数倍时间,并且客户存储本地的所有历史日志的负担太重)。最终,在主网络内部署的策略将有可能是带有初始强制receipt Merkle证明的全节点策略,随着时间的推移,松散支持越来越多的中型节点。
注意,不需要将Merkle证明作为一个数据包,从一个分片直接导入另一个分片,反而是,所有的证明传输逻辑均在验证者和客户端层面处完成,并且在协议层面实现了一个接口,通过该接口可以访问Merkle。长时间ASYNC_DELAY减少了一个分片内改组的可能性,这将需要整个通道的集中改组。
如果需要短时间延迟,可以在协议之上实施的一种机制是分片内投注市场,例如,在分片j内,A可以和B打一个赌,比如说“如果在分片i内,区块X被敲定,B同意向A发送0.001 ETH,反之,如果在分片i内,区块X未被敲定,则A同意向B发送1000 ETH”。针对此目的,Casper保证金可以有双重用途-即使在分片j内进行打赌,A赌输的信息将通过receipt传输给分片0,随后一旦A提取,分片0将转移1000以太给B。B将因此获信:A足够确信其他分片的区块将被敲定并据此而做出投注,同时B也获得了防止A判断失败的一种保险(即使运用了双重用途方案,该保险还是有缺陷的,如果A是恶意的,那么他们将输掉他们全部的赌注,这样B将什么也得不到)。该方案存在的一个可扩展限制,与一个节点计算能力的平方成比例。原因有二,首先,与分片数量成比例的计算能力总数必须在分片0上计算奖励。其次,所有客户端必须为所有分片的轻客户端。因此,假设节点的计算能力为N,则应该存在O(N)个分片并且每个分片的处理能力均为O(N),其网络总处理能力为O(N^2)。超过这个最大值,将需要一种更加复杂的分片协议以构成某种树结构的声明验证,这超出了本文的范围。
译者&来源:LinktimeTech 微信公众号
在刚刚闭幕的以太坊Devcon 2大会上,以太坊创始人Vitalik分享了他的最新研究成果《以太坊紫皮书》,在此我们将其翻译成中文,供大家阅读。《紫皮书英文》原版大家也可以从我们的公众号中找到并阅读。当然,如此前沿和复杂的论文翻译是很大的考验,如果大家觉得以下内容有问题,请及时与我们反馈,我们将在后期的公众号文章中进行纠正。论文全文内容如下:
(为什么叫紫皮书?)
前言
在过去十年中,诸如Bitcoin、Namecoin和以太坊这些项目充分展现了密码经济共识网络对促成下一代去中心化系统的强大推力,并潜移默化的将开发视野从简单的数据存储和信息服务扩展到任何状态应用的后台管理。基于这个系统,在全球范围内提议和执行的应用,涵盖了全球低价支付系统,金融合同,市场预测,身份注册和现实世界产权,建立更安全的证书管理系统,甚至通过供应链对制造品进行溯源和跟踪管理。
但是,这个系统的技术基础仍然存在严重的效率问题。因为在网络上每个全节点都必须维护整个系统的状态和处理每个交易,整个区块网络效率受限于单个计算节点。现在大部分系统所采用的共识机制,工作量证明(POW),需要消耗大量的电力去运营; 基于POW机制的最大的工作区块链Bitcoin,消耗了相当于整个爱尔兰的用电量。
这篇文章为上述问题提供将POS和基于分片证明进行合并的解决方案。POS本身并不是一个新奇的主意,2011年就已经存在,但新的算法展现了实质性的好处,不仅解决了前一系统的缺陷,甚至拥有POW不曾有的属性。
POS可以被想象成一种虚拟挖矿。然而在POW模式下,用户需要花费一定的金钱买一台电脑,然后消耗真实的电力,其随机获得区块链的成本与消耗的电力大致成比例。在POS模式下,用户花费金钱购买系统内的虚拟代币,然后用一个内部协议机制将虚拟代币转换成虚拟电脑,系统模拟随机产生的区块的成本与购买成本大致成比例,达到了POW同样的出块效果,却不用消耗电力。
分片也并不新颖,在现行的分布式数据库设计中有超过10年的应用,但是到目前为止,研究将其应用在区块链上,仍有颇多限制。基本的路径是解决可扩展的挑战, 通过架构中的全球验证程序集合中的节点(在我们的情况下,通过股权结合证明了)被随机分配到特定的“碎片”,其中每个碎片并行处理全局状态的不同部分,从而确保工作是跨节点分布处理,而不是每个节点都重复做。
我们渴望实现以下目标
我们从描述一个算法开始,该算法实现目标1和2, 然后在第二个算法实现了目标3, 然后在第三个算法中一定程度上实现目标4,5(作为一个限制条件,对一个节点的计算能力的平方大致成比例,如(4)和一个24小时的延迟跨碎片信息,有可能建立更快的消息作为一个层上通过双用途的存款,在例(5)。对目标(4)和(5)的更强级别的满意,(6)也一样,使得重新设计2.1和3.0。
常数
我们设置:
最小的POS
(注意,后续内容假设读者有对以太坊1.0的基本了解)
我们可以创建一个最小可行的PoS算法,没有诸如敲定确定、额外的抗审查以及分片等特性。在地址CASPER_ADDRESS存在一个合约,其主要功能是追踪验证者集合的变化。该合约没有特殊的特权,除了调用该合约是验证区块标头过程中的一部分,而且是包括在创世区块,而不是通过交易被动态添加的。验证者集合初始设置在创世区块中,并可以通过以下函数进行调整:
接受一定量的以太币作为保证金,发送者指定一段验证代码(本质为EVM字节码,代码主要功能是用作一种公钥,以便后续其他的节点验证由他们进行签名的区块数据以及相关的网络共识消息),一个随机提交的交易(一个32字节的哈希用于验证者的选择;详见下文)和最终提现地址一并发送。值得注意的是,提现可以在一个特定的地址发送,该地址的合约功能唯一用途就是特定的地址条件下释放资金,如果需要还可以双重用保证金。如果所有参数都被接受,在下一个时期将增加该验证者到验证者集合。(例如,如果保证金在第N个时期请求提取, 而验证者在第N+2个时期被加入验证者集合,这个一个时期等于EPOCH_LENGTH个区块时间周期)。验证代码的哈希值(叫做vchash)可以被验证者用作识别号;不同验证者具有同样的哈希值是被禁止的。)
startWithdrawal(bytes32 vchash, bytes sig):
开始撤回流程,要求一个签名,该签名需要通过验证者的验证代码。如果签名通过,从下一个时期开始,验证者被从验证者集撤回。注意,这个函数不退回以太币。
还有一个函数:
withdraw(bytes32 vchash):
撤回验证者的以太币到指定的提现地址,增加奖励减少惩罚,只要验证者已经从验证者活动集合中通过使用 StartWithdrawal至少是WITHDRAWAL_DELAY几秒前撤回。
准确的讲,验证代码就像是放入区块标头的哈希码,加上签名,如果签名有效返回1,反之,则返回0。这个机制确保了我们不会将验证者锁定到任何一个特殊签名的算法,相反允许验证者使用验证代码从多重私钥验证签名取代单一验证,也允许使用Lamport签名对抗量子计算机的攻击。这个代码在黑匣子环境下通过使用新的CALL_BLACKBOX操作码执行,以保证执行独立于外部状态。这样可以防止一些攻击,如一个验证者创建了验证代码在状态良好的时候返回1,在状况不好的时候(例如 Dunkle inclusion)返回0.
deposit函数中randao 参数的值应该是计算一段长链哈希值结果,即设置秘密随机的X,执行计算randao = sha3(sha3(sha3(sha3(.....(sha3(x))…))。每个验证者提供randao的值保存在Casper 合约中的存储空间。
Casper合约也含有一个叫GlobalRandao的变量,初始化为0。这个合约含有一个函数getValidator(uint256 skips):返回 skips 跳过以后的验证者的代码。例如 getValidator(0)返回第一个验证者(验证者一般来讲可以创造区块),getValidator(1)返回第二个验证者(如果第一个不能创建区块,验证者可以创建)
每个验证者都是通过伪随机算法从目前活动的验证者集合进行选择,随机对初始保证金规模进行加权,并以Casper 合约中的globalRandao值为伪随机种子。除了签名外,一个有效的区块也必须包含为那个验证者目前保存Randao的原像。这个原像然后替换保存Randao值,然后也通过异或运算保存到合约的globalRandao值中。这样,一个验证者生成的每一区块都要求脱掉一个验证者的randao的一层。这是一种基于在这里解释( http://vitalik.ca/files/randomness.html )的随机算法。
总之,一个区块必须包含的如下的额外数据:
<vchash><randao><sig>
其中,vchash是验证代码的32字节哈希值,用于快速识别验证者。randao含义如上所描述(也是32字节),sig是签名,可以是任意长度(虽然我们将区块标头的大小限制为2048字节)。
创建区块所需要的最短时间可以简单的定义为: GENESIS_TIME + BLOCK_TIME * <block height> + SKIP_TIME * <自创世区块后所有的验证者跳数和>。
在实际过程中,这意味着,一旦发布了某个区块,那么下一个区块的0-skip验证者会在BLOCK_TIME秒之后发布,同理,1-skip验证者则在BLOCK_TIME + SKIP_TIME秒之后发布,以此类推。
如果一个验证者发布一个区块太早,其他的验证者会忽视该区块,直到在规定的时间之后,才会处理该区块(该中机制的进一步描述和验证详见这里( http://vitalik.ca/files/timing.html);短BLOCK_TIME和长SKIP_TIME之间的不对称性,可以确保:在正常情况下,区块的平均保留时间可以非常短,而在网络延迟更长的情况下,也可以保证网络的安全性)。
如果一个验证者创建了一个包括在链内的区块,他们得到的区块奖励相当于此周期内活动验证者集合内以太的总量乘以REWARD_COEFFICIENT * BLOCK_TIME。因此,如果验证者总是正确的履行职责,REWARD_COEFFICIENT本质上成为验证者的“预期每秒收益率”;乘以~3200万得到近似的年化收益率。如果一个验证者创建了一个不包括在链内的区块,之后,在将来的任意时间(直到验证者调用withdraw函数为止)该区块标头可以作为一个“dunkle”通过Casper合约的includeDunkle函数,被包括在链内;这使得验证者损失了相当于区块奖励的钱数(以及向包括dunkle在内的当事方提供一小部分罚款作为激励)。因此,验证者应当在确定该区块在链内的可能性超过50%,才实际创建该区块;该机制不鼓励所有链上的验证。验证者的累计保证金,包括奖励和罚款,存储在Casper合约的状态内。
“dunkle”机制的目的是为了解决权益证明中的“零赌注”的问题,其中,如果没有罚款,只有奖励,那么验证者将被物质激励,试图在每个可能的链上创建区块。在工作量证明场景下,创建区块需要成本,并且只有在“主链”上创建区块才有利可图。Dunkle机制试图复制工作量证明中的经济理论,针对创建非主链上区块进行人工罚款,来代替工作量证明中电费用的“自然罚款”。
假设一个固定大小的验证者集合,我们可以很容易的定义分叉选择规则:计算区块数,最长链胜出。假设验证者集合可以变大和缩小,但是,该规则就不太适用了,因为作为少数支持的分叉的速度一个时期以后将像多数支持的分叉一样。因此,我们可以用计算的区块数代替定义的分叉选择规则,给每个区块一个相当于区块奖励的权重。因为区块奖励与积极验证的以太总量成正比,这确保了更积极验证以太的链得分增长速度更快。
我们可以看出,这条规则可以用另一种方式很方便的理解:基于价值损失的分叉选择模型。该原则是:我们选择验证者赌最多价值的链,就是说,验证者承认除了上述的链外,所有其他链都会损失大量的资金。我们可以等同认为,这条链是验证者失去资金最少的链。在这样一个简单的模式中,很容易看出这如何简单的对应着区块权重为区块奖励的最长链。该算法是尽管简单,但用于股权证明的实现来说也足够高效。
添加敲定
下一步是增加经济敲定的概念。我们按照以下方式进行。在区块标头内部,除了指向上一个区块的哈希之外,现在验证者也对某个以前区块FINALIZATION_TARGET敲定概率做出了一个断言。该断言被当做一个赌注,如“我相信区块0x5e81d……将被敲定,并且在所有区块史中,如果这一断言是错误的,我愿意损失V_LOSS,假设在所有区块史中,这一断言是正确的,我将得到V_GAIN。”验证者选择odds参数,而V_LOSS和V_GAIN,按照如下方式计算(令total_validating_ether为活动验证者集合的以太总量,MAX_REWARD是允许的区块奖励的最大值,bet_coeff是下文定义的系数):
V_GAIN and V_LOSS values relative to the BASE_REWARD, assuming bet_coeff = 1,假设bet_coeff = 1,V_GAIN值和V_LOSS值与BASE_REWARD的相关性
FINALIZATION_TARGET从null开始, 但是区块1期间,它被设置成区块0,。bet_coeff初始(开始)值被设置为1,另一个变量cached_bet_coeff设置为0。但是bet_coeff不能减少到低于MIN_BET_COEFF,在每个区块,我们设置bet_coeff -= bet_coeff / FINALITY_REWARD_DECAY_FACTOR,cached_bet_coeff -= cached_bet_coeff / FINALITY_REWARD_DECAY_FACTOR(这确保了总有激励驱动去打赌)。当创建一个区块,验证者得到BASE_REWARD * log(MAXODDS) * cached_bet_coeff,其中MAXODDS是最大的可能赔率,如MAX_DEPOSIT_SIZE / BASE_REWARD。该机制随所实现的是,一旦某一区块被确定被敲定,验证者从中得到奖励,就像他们以最大odds继续进行验证一样。这确保了,验证者不会受到不正当的激励而共谋通过延迟敲定某个区块以获得最大收益。
当Casper合约确定FINALIZATION_TARGET已被敲定(即已知区块的总价值损失超过某个阈值),我们将新的FINALIZATION_TARGET设置成当前区块,我们设置cached_bet_coeff += bet_coeff,并且bet_coeff重新设置为1。从下一个区块开始,敲定过程重新开始设置新的FINALIZATION_TARGET。如果,有一个短链分裂,敲定过程可能同时处理多个区块,甚至是不同高度的区块;但是,鉴于区块投注的默认验证者策略,以及支持它的最高价值损失,我们预计收敛过程倾向于选择他们其中的一个(此处的收敛参数基本上与最小权益证明相一致)。
当新的区块敲定过程开始启动时,我们期望最初的赔率很低,这表示验证者对短范围分叉的恐惧,但是随着时间的推移,验证者愿意投注的赔率会逐渐增加。特别的如果他们看到其他验证者对该区块进行高赔率投注,验证者的赌注也会增加。可以预计,该区块的价值损失将以指数倍增加,从而在对数时间内,达到最大“总保证金损失”。
在区块标头的额外数据中,我们现在将所需的数据格式更改为以下格式:
<vchash><randao><blockhash><logodds><sig>
其中区块哈希是上个打赌的区块的哈希,并且logodds为1字节值,代表对数形式的赔率(即,0对应1,8对应2,16对应4等)。
请注意,我们不能允许验证者完全自由地设置赔率。考虑如下场景,如果有两个竞争关系的敲定目标,B1和B2(即存在两个链,其中一个的FINALIZATION_TARGET设置为B1,另一个的的FINALIZATION_TARGET设置为B2),并且围绕B1开始形成共识,然后一个恶意验证者可能会突然对B2投入一个高赔率赌注,它的价值损失足够影响共识,从而引发短范围分叉。因此,我们使用以下规则,通过限制V_LOSS来限制赔率:
该规则设计上引入了一个安全约束:当至少(样本)三分之二的其它验证者风险为x后,当前验证者的风险仅可以为1.5x。这与拜占庭容错共识算法的事先承诺或承诺模式类似,一个验证者应等待其他三分之二的验证者完成给定的步骤后,才能进行下一步,并确保一定的安全,也确保大多数共谋不能参与“恶意破坏”攻击(如,让其他的验证者讲大量赌注添加在一个区块,并且随后推动共识到一个别的区块),无法共谋,因为共谋本身需要耗费大量资金(实际上,共谋损失金钱的速度比受害者损失金钱的速度更快,这是一个显著的特性,因为它确保,在大多数敌对情况下,恶意操作者会随着时间的推移,经常会被“淘汰”。)
如果一个区块作为一个dunkle被加入链中,赌注被处理,并且罚款和奖励也会产生。例如,如果有两个高度为5000的区块A1和A2,互为竞争的敲定目标,并且有两个高度为5050的区块B1和B2(两区块均以A1为上一代区块),并且验证者在B1上构建区块C,对A1下赌注,随后,如果B2最终确定为主链区块,B1和C将成为dunkle,并且C也将因B1 vs B2下错注而被惩罚,但是还是会因A1被赌对而进行奖励。
然而,假设C中的V_LOSS是这样的,如果B1包含在内,则V_LOSS < V_LOSS_MAX;如果B2包含在内,则V_LOSS > V_LOSS_MAX。之后,为了保证预期的价值损失特性,我们制定了一个额外的罚款:即使验证者赌对了,但我们仍以V_LOSS - V_LOSS_MAX对他们进行处罚。因此,我们有效地将V_LOSS赌注,分解成(i)价值损失V_LOSS_MAX的赌注和(ii)V_LOSS - V_LOSS_MAX的单纯价值销毁,从而保证这种规模过大的赌注仍仅通过V_LOSS_MAX经由分叉选择规则变换。这意味着投注在某种意义上是不“完全的”,因为如果该区块的子代区块中的很多都分叉了,即使该区块已经完成敲定确定,某个区块的赌注仍可能引起罚款。在投注模型中的单纯价值损失,被认为是对价值损失分叉选择规则利润纯度的一个可接受的权衡。
计分与策略实现
价值损失计分可以通过以下算法实现:
此处用于举例的V_LOSS;在现实中,他们不会被允许这样快速的增长,并且B或C会要求A上的较高的V_LOSS成为敲定候选区块。
一个简单的验证者策略是仅在头部创建区块,并且做出敲定赌注,此处的价值损失是所描述的最大价值损失的80%。
轻客户端同步
敲定机制开启了一个快速轻客户端同步算法的大门。该算法包括以下几步:
非常值得关注的是,通过步骤1到5可以用两个网络请求和几秒钟计算来验证全天的区块。
分片
现在,我们考虑从一个分片扩大到多个分片。我们构建的模型如下。取代已有的单条区块链,我们现在拥有被我们称为“分片”的多条相关联的区块链。NUM_SHARDS分片的编号为分片0至分片NUM_SHARDS – 1,其中分片0简单的作为常规股权证明区块链进行,其确定性如上所述,但是分片1……NUM_SHARDS – 1工作机制有所不同。在每个时期的开始,为每个分片随机挑选VALIDATORS_PER_SHARD个验证者,并且被分配为下一个时期的验证者(如,n+1时期的验证者在n时期时被指配)。当调用getValidator(skip)以确定这些分片内一个分片的验证者,仅仅随机从选取的验证者集合中选择一个验证者(平等地分配,因为在挑选的时候就已经对保证金大小进行加权)。分片1……NUM_SHARDS – 1的确定性赌注并未在分片内做出,而是在分片0内做出。当赌注被做出,它就被存储,赌注仅在子代区块周期结束后,才被处理(如,n+1时期的区块确定断言,将在n+3区块周期开始时,在分片0内进行处理)。
如果已经为一个分片挑选了一个验证者,那么这个验证者将需要调用Casper合约的registerForShard(bytes32 vchash,uint256 shard,uint256 index,bytes32 randao)函数,其中vchash是验证者的验证代码哈希,shard是分片ID,index是一个数值且0 <= index < VALIDATORS_PER_SHARD,其中得getShardValidator(uint256 shard,uint256 index)返回给定的验证代码哈希,并且randao是一个randao承诺。为了引导验证者,该函数生成了一个收据,该收据能够通过利用confirmReceipt(uint256 receiptId)在目标分片上进行确认。
getShardValidator依赖于一个单独的随机源,业务逻辑与getValidator类似。该随机源,按照如下步骤得出:
使用Iddo Bentov的低影响函数,增加了随机源的操纵成本,因为,这个特别的随机源种子会产生实质性的经济结果,因此,它比正常操纵目标更大一些。
跨分片敲定赌注并不在区块标头上,所以不会过度的阻碍轻客户端;相反,验证者将创建一个交易,在它们创建的任何区块间,调用一个registerFinalityBets(bytes32[] hashes,bytes logodds)函数,任意一个区块中创建预期的NUM_SHARDS个哈希和一个长度为NUM_SHARDS的字节数组,每个字节代表相应的区块哈希的赔率。
验证者的典型工作流程是维持分片0的一个“全节点”,并且保持追踪被分配给它们的未来分片。如果一个验证者被分配给一个分片,他们将利用Merkle树证明下载状态,并且确保当他们需要开始验证时,他们已经下载了相应状态。对于该时期,他们作为该分片的验证者,并且创建区块,同时,他们将在所有的分片上做出敲定赌注,他们通过观测(i)每个分片上的最长链,(ii)其他的验证者的敲定赌注,以及(iii)在片区内试图达到51%成功攻击的各种二次启发方法和机制(如,欺诈证明)。注意,分配到任何给定分片的概率与验证者的累计以太成比例,因此,如果验证者的资产成倍,那么需要处理的计算也会加倍。这个特性被认为是何意的,因为它增加了公平性,并降低了矿池激励,同时引入了一个原则,该原则下处理交易并存储区块链本身也成为了“工作量混合证明”的一种形式。
取样机制的初衷是为了确保系统仅依靠少量验证者进行实际交易验证,同时系统能够安全应对累积以太保证金高达~33-40%(低于50%,因为总累积以太为33-50%的攻击者在某些给定的分片会很“幸运”)的攻击者;因为取样是随机的,攻击者不能够选择将他们的权益集中在一个分片上,许多工作量分片方案的一个致命缺陷上。即使一个分片被攻击了,还有第二道防线:如果其他验证者发现攻击的证据,那么他们可以拒绝做出跟随攻击者分叉的敲定声明,而确认由诚实节点创建的链。如果一个分片上的一个攻击者试图从无效区块创建一个链,其他分片的验证者可以检测到,并且之后暂时完全验证该分片上的节点,并且确保他们仅敲定有效的区块。
跨分片通信
本方案中的跨分片通信遵循如下工作。我们创建一个ETHLOG操作码(带有两个参数:to value),其创建的一个日志记录,其存储内容是空字符串(注意,该空字符串,不是32个零字节,一个传统的LOG只能存储32字节的字符串),它的数据是一个64字节的字符串,包括目标和具体值。我们创建的GETLOG操作码,需要一个由区块定义ID的单独的参数。number * 2**64 + txindex * 2**32 + logindex(其中txindex是交易的index,包括区块的log,logindex是交易receipt中的log index)试图获取指定的存储内容,存储在状态中的日志表明,该日志已被消耗,并且将日志数据放在目标数组内。如果日志值为空串,这也将传输以太给收件人。为了成功得到日志内容,调用操作码的交易必须携带日志ID参数。如果v=0,我们允许签名中的r值重新用于此目的(注意:这意味着此处仅可以使用EIP 86交易;我们希望到现在为止,EIP 86交易将是交易的主要形式)。
现在抽象共识再也不是一个单链,而是一个链的集合,c[0]…c[NUM_SHARDS - 1]。状态转移函数不再是stf(state, block) -> state',而是stf(state_k, block, r_c[0]…r_c[NUM_SHARDS - 1]) ->state_k,其中
复制代码
是来自过去的超过ASYNC_DELAY区块的i链的receipt集合。
请注意,有几种方法可以“满足”这种抽象。一种方法是“每个节点都是全功能节点”的方法:所有节点存储所有分片的状态,更新所有分片的链,因此拥有足够的信息,可以计算所有的通道交易函数。然而,这是令人乏味的,因为它是不可扩展的。
更有趣的策略是“中型节点”方法:大多数节点选择一些分片,保持更新到最新(尽可能包括分片0),并作为所有其他分片的轻客户端。当计算通道交易函数时,他们需要旧交易收据,然而,他们并没有存储旧交易收据,为此,我们增加了一个网络协议规则,要求交易伴随着任何交易静态参考的收据的Merkle证明(这里为什么需要静态参考就很清楚了:否则,在运行时间创建的任意一个GETLOG操作,因为网络延迟,日志读取数据将变成一个缓慢的过程,将耗费数倍时间,并且客户存储本地的所有历史日志的负担太重)。最终,在主网络内部署的策略将有可能是带有初始强制receipt Merkle证明的全节点策略,随着时间的推移,松散支持越来越多的中型节点。
注意,不需要将Merkle证明作为一个数据包,从一个分片直接导入另一个分片,反而是,所有的证明传输逻辑均在验证者和客户端层面处完成,并且在协议层面实现了一个接口,通过该接口可以访问Merkle。长时间ASYNC_DELAY减少了一个分片内改组的可能性,这将需要整个通道的集中改组。
如果需要短时间延迟,可以在协议之上实施的一种机制是分片内投注市场,例如,在分片j内,A可以和B打一个赌,比如说“如果在分片i内,区块X被敲定,B同意向A发送0.001 ETH,反之,如果在分片i内,区块X未被敲定,则A同意向B发送1000 ETH”。针对此目的,Casper保证金可以有双重用途-即使在分片j内进行打赌,A赌输的信息将通过receipt传输给分片0,随后一旦A提取,分片0将转移1000以太给B。B将因此获信:A足够确信其他分片的区块将被敲定并据此而做出投注,同时B也获得了防止A判断失败的一种保险(即使运用了双重用途方案,该保险还是有缺陷的,如果A是恶意的,那么他们将输掉他们全部的赌注,这样B将什么也得不到)。该方案存在的一个可扩展限制,与一个节点计算能力的平方成比例。原因有二,首先,与分片数量成比例的计算能力总数必须在分片0上计算奖励。其次,所有客户端必须为所有分片的轻客户端。因此,假设节点的计算能力为N,则应该存在O(N)个分片并且每个分片的处理能力均为O(N),其网络总处理能力为O(N^2)。超过这个最大值,将需要一种更加复杂的分片协议以构成某种树结构的声明验证,这超出了本文的范围。
译者&来源:LinktimeTech 微信公众号
在刚刚闭幕的以太坊Devcon 2大会上,以太坊创始人Vitalik分享了他的最新研究成果《以太坊紫皮书》,在此我们将其翻译成中文,供大家阅读。《紫皮书英文》原版大家也可以从我们的公众号中找到并阅读。当然,如此前沿和复杂的论文翻译是很大的考验,如果大家觉得以下内容有问题,请及时与我们反馈,我们将在后期的公众号文章中进行纠正。论文全文内容如下:
![以太坊紫皮书(中文版)](http://8btc.com/data/attachment/forum/201609/25/231030scmovsvno9n0bnfv.jpg)
(为什么叫紫皮书?)
前言
在过去十年中,诸如Bitcoin、Namecoin和以太坊这些项目充分展现了密码经济共识网络对促成下一代去中心化系统的强大推力,并潜移默化的将开发视野从简单的数据存储和信息服务扩展到任何状态应用的后台管理。基于这个系统,在全球范围内提议和执行的应用,涵盖了全球低价支付系统,金融合同,市场预测,身份注册和现实世界产权,建立更安全的证书管理系统,甚至通过供应链对制造品进行溯源和跟踪管理。
但是,这个系统的技术基础仍然存在严重的效率问题。因为在网络上每个全节点都必须维护整个系统的状态和处理每个交易,整个区块网络效率受限于单个计算节点。现在大部分系统所采用的共识机制,工作量证明(POW),需要消耗大量的电力去运营; 基于POW机制的最大的工作区块链Bitcoin,消耗了相当于整个爱尔兰的用电量。
这篇文章为上述问题提供将POS和基于分片证明进行合并的解决方案。POS本身并不是一个新奇的主意,2011年就已经存在,但新的算法展现了实质性的好处,不仅解决了前一系统的缺陷,甚至拥有POW不曾有的属性。
POS可以被想象成一种虚拟挖矿。然而在POW模式下,用户需要花费一定的金钱买一台电脑,然后消耗真实的电力,其随机获得区块链的成本与消耗的电力大致成比例。在POS模式下,用户花费金钱购买系统内的虚拟代币,然后用一个内部协议机制将虚拟代币转换成虚拟电脑,系统模拟随机产生的区块的成本与购买成本大致成比例,达到了POW同样的出块效果,却不用消耗电力。
分片也并不新颖,在现行的分布式数据库设计中有超过10年的应用,但是到目前为止,研究将其应用在区块链上,仍有颇多限制。基本的路径是解决可扩展的挑战, 通过架构中的全球验证程序集合中的节点(在我们的情况下,通过股权结合证明了)被随机分配到特定的“碎片”,其中每个碎片并行处理全局状态的不同部分,从而确保工作是跨节点分布处理,而不是每个节点都重复做。
我们渴望实现以下目标
- 通过POS提升效率:共识机制不应该由挖矿进行保证,从而大大减少电力浪费,并且可以满足大量和持续发行ETH的需要。
- 快速的出块时间:在不威胁安全的前提下,出块速度达到最大值。
- 经济一致:一旦区块被制作,经过一定时间和事件的处理,大部分的验证者将‘全提交’那个区块,意味着他们将损失全部的以太币保证金在没有包含这个区块的历史记录(想想1000万价值的以太币),这是非常需要的,因为意味着大部分的碰撞不能通过节点进行传递或者不用破坏掉以太币就可以51%攻击。默认的验证者战略是被设计成保守的他们愿意做出高价值的承诺,诚信的验证者的风险应该很低。
- 可扩展性:应该不需要跑所有节点就可以运行区块链,例如在这样的情况下,所有节点包括验证节点只保持一小部分区块的数据碎片,然后用轻客户端技术访问剩余部分的区块。采用方式下,相对单个节点处理能力,区块链可以达到更高的交易处理吞吐量,同时运行区块平台所需的只是大量的普通个人电脑,因此也可以保证去中心化。
- 跨碎片通信:构建这样一类应用并实现应用之间的互操作性在理论上是具有最大可行性的。该类应用的资源使用达到一个临界点以至超出单个节点的计算能力和带宽限制,并且分别存储在不同节点和处于不同状态。
- 抵抗计算审查:该协议设计为可抵抗大部分的恶意验证节点跨越所有碎片而发起的联合攻击,使得无效交易不能被打包到区块并成为整个区块链的一部分。在某种程度上,可以通过以太坊1.0的停机问题存在,但是我们可以通过引入保证调度的概念和保证交叉碎片信息使这个机制更加强健。
我们从描述一个算法开始,该算法实现目标1和2, 然后在第二个算法实现了目标3, 然后在第三个算法中一定程度上实现目标4,5(作为一个限制条件,对一个节点的计算能力的平方大致成比例,如(4)和一个24小时的延迟跨碎片信息,有可能建立更快的消息作为一个层上通过双用途的存款,在例(5)。对目标(4)和(5)的更强级别的满意,(6)也一样,使得重新设计2.1和3.0。
常数
我们设置:
- BLOCK_TIME: 4 seconds (aiming on the less ambitious side to reduce overhead,针对不太有野心的目标去减少开销)
- SKIP_TIME: 8 seconds (aiming on the less ambitious side to reduce overhead针对不太有野心的目标去减少开销)
- EPOCH_LENGTH: 10800 (ie. 12 hours under good circumstances,12小时的良好状态)
- ASYNC_DELAY: 10800 (ie. 12 hours under good circumstances12小时的良好状态)
- CASPER_ADDRESS: 255
- WITHDRAWAL_DELAY: 10000000, ie. 4 months
- GENESIS_TIME: some future timestamp marking the start of the blockchain, say 1500000000
- REWARD_COEFFICIENT: 3 / 1000000000
- MIN_DEPOSIT_SIZE: 32 ether
- MAX_DEPOSIT_SIZE: 131072 ether
- V_LOSS_MAXGROWTH_FACTOR: 32
- FINALITY_REWARD_COEFFICIENT: 0.6 / 1000000000
- FINALITY_REWARD_DECAY_FACTOR: 1000 (ie. 1.1 hours under good circumstances)
- MIN_BET_COEFF: 0.25
- NUM_SHARDS: 80
- VALIDATORS_PER_SHARD: 120
最小的POS
(注意,后续内容假设读者有对以太坊1.0的基本了解)
我们可以创建一个最小可行的PoS算法,没有诸如敲定确定、额外的抗审查以及分片等特性。在地址CASPER_ADDRESS存在一个合约,其主要功能是追踪验证者集合的变化。该合约没有特殊的特权,除了调用该合约是验证区块标头过程中的一部分,而且是包括在创世区块,而不是通过交易被动态添加的。验证者集合初始设置在创世区块中,并可以通过以下函数进行调整:
- deposit(bytes validation_code, bytes32 randao, address withdrawal_address): 保证金(字节validation_code,bytes32 randao,地址withdrawal_address)
接受一定量的以太币作为保证金,发送者指定一段验证代码(本质为EVM字节码,代码主要功能是用作一种公钥,以便后续其他的节点验证由他们进行签名的区块数据以及相关的网络共识消息),一个随机提交的交易(一个32字节的哈希用于验证者的选择;详见下文)和最终提现地址一并发送。值得注意的是,提现可以在一个特定的地址发送,该地址的合约功能唯一用途就是特定的地址条件下释放资金,如果需要还可以双重用保证金。如果所有参数都被接受,在下一个时期将增加该验证者到验证者集合。(例如,如果保证金在第N个时期请求提取, 而验证者在第N+2个时期被加入验证者集合,这个一个时期等于EPOCH_LENGTH个区块时间周期)。验证代码的哈希值(叫做vchash)可以被验证者用作识别号;不同验证者具有同样的哈希值是被禁止的。)
startWithdrawal(bytes32 vchash, bytes sig):
开始撤回流程,要求一个签名,该签名需要通过验证者的验证代码。如果签名通过,从下一个时期开始,验证者被从验证者集撤回。注意,这个函数不退回以太币。
还有一个函数:
withdraw(bytes32 vchash):
撤回验证者的以太币到指定的提现地址,增加奖励减少惩罚,只要验证者已经从验证者活动集合中通过使用 StartWithdrawal至少是WITHDRAWAL_DELAY几秒前撤回。
准确的讲,验证代码就像是放入区块标头的哈希码,加上签名,如果签名有效返回1,反之,则返回0。这个机制确保了我们不会将验证者锁定到任何一个特殊签名的算法,相反允许验证者使用验证代码从多重私钥验证签名取代单一验证,也允许使用Lamport签名对抗量子计算机的攻击。这个代码在黑匣子环境下通过使用新的CALL_BLACKBOX操作码执行,以保证执行独立于外部状态。这样可以防止一些攻击,如一个验证者创建了验证代码在状态良好的时候返回1,在状况不好的时候(例如 Dunkle inclusion)返回0.
deposit函数中randao 参数的值应该是计算一段长链哈希值结果,即设置秘密随机的X,执行计算randao = sha3(sha3(sha3(sha3(.....(sha3(x))…))。每个验证者提供randao的值保存在Casper 合约中的存储空间。
Casper合约也含有一个叫GlobalRandao的变量,初始化为0。这个合约含有一个函数getValidator(uint256 skips):返回 skips 跳过以后的验证者的代码。例如 getValidator(0)返回第一个验证者(验证者一般来讲可以创造区块),getValidator(1)返回第二个验证者(如果第一个不能创建区块,验证者可以创建)
每个验证者都是通过伪随机算法从目前活动的验证者集合进行选择,随机对初始保证金规模进行加权,并以Casper 合约中的globalRandao值为伪随机种子。除了签名外,一个有效的区块也必须包含为那个验证者目前保存Randao的原像。这个原像然后替换保存Randao值,然后也通过异或运算保存到合约的globalRandao值中。这样,一个验证者生成的每一区块都要求脱掉一个验证者的randao的一层。这是一种基于在这里解释( http://vitalik.ca/files/randomness.html )的随机算法。
总之,一个区块必须包含的如下的额外数据:
<vchash><randao><sig>
其中,vchash是验证代码的32字节哈希值,用于快速识别验证者。randao含义如上所描述(也是32字节),sig是签名,可以是任意长度(虽然我们将区块标头的大小限制为2048字节)。
![以太坊紫皮书(中文版)](http://8btc.com/data/attachment/forum/201609/25/231434sg2fr36rzzi22z4q.png)
创建区块所需要的最短时间可以简单的定义为: GENESIS_TIME + BLOCK_TIME * <block height> + SKIP_TIME * <自创世区块后所有的验证者跳数和>。
在实际过程中,这意味着,一旦发布了某个区块,那么下一个区块的0-skip验证者会在BLOCK_TIME秒之后发布,同理,1-skip验证者则在BLOCK_TIME + SKIP_TIME秒之后发布,以此类推。
如果一个验证者发布一个区块太早,其他的验证者会忽视该区块,直到在规定的时间之后,才会处理该区块(该中机制的进一步描述和验证详见这里( http://vitalik.ca/files/timing.html);短BLOCK_TIME和长SKIP_TIME之间的不对称性,可以确保:在正常情况下,区块的平均保留时间可以非常短,而在网络延迟更长的情况下,也可以保证网络的安全性)。
如果一个验证者创建了一个包括在链内的区块,他们得到的区块奖励相当于此周期内活动验证者集合内以太的总量乘以REWARD_COEFFICIENT * BLOCK_TIME。因此,如果验证者总是正确的履行职责,REWARD_COEFFICIENT本质上成为验证者的“预期每秒收益率”;乘以~3200万得到近似的年化收益率。如果一个验证者创建了一个不包括在链内的区块,之后,在将来的任意时间(直到验证者调用withdraw函数为止)该区块标头可以作为一个“dunkle”通过Casper合约的includeDunkle函数,被包括在链内;这使得验证者损失了相当于区块奖励的钱数(以及向包括dunkle在内的当事方提供一小部分罚款作为激励)。因此,验证者应当在确定该区块在链内的可能性超过50%,才实际创建该区块;该机制不鼓励所有链上的验证。验证者的累计保证金,包括奖励和罚款,存储在Casper合约的状态内。
“dunkle”机制的目的是为了解决权益证明中的“零赌注”的问题,其中,如果没有罚款,只有奖励,那么验证者将被物质激励,试图在每个可能的链上创建区块。在工作量证明场景下,创建区块需要成本,并且只有在“主链”上创建区块才有利可图。Dunkle机制试图复制工作量证明中的经济理论,针对创建非主链上区块进行人工罚款,来代替工作量证明中电费用的“自然罚款”。
假设一个固定大小的验证者集合,我们可以很容易的定义分叉选择规则:计算区块数,最长链胜出。假设验证者集合可以变大和缩小,但是,该规则就不太适用了,因为作为少数支持的分叉的速度一个时期以后将像多数支持的分叉一样。因此,我们可以用计算的区块数代替定义的分叉选择规则,给每个区块一个相当于区块奖励的权重。因为区块奖励与积极验证的以太总量成正比,这确保了更积极验证以太的链得分增长速度更快。
我们可以看出,这条规则可以用另一种方式很方便的理解:基于价值损失的分叉选择模型。该原则是:我们选择验证者赌最多价值的链,就是说,验证者承认除了上述的链外,所有其他链都会损失大量的资金。我们可以等同认为,这条链是验证者失去资金最少的链。在这样一个简单的模式中,很容易看出这如何简单的对应着区块权重为区块奖励的最长链。该算法是尽管简单,但用于股权证明的实现来说也足够高效。
添加敲定
下一步是增加经济敲定的概念。我们按照以下方式进行。在区块标头内部,除了指向上一个区块的哈希之外,现在验证者也对某个以前区块FINALIZATION_TARGET敲定概率做出了一个断言。该断言被当做一个赌注,如“我相信区块0x5e81d……将被敲定,并且在所有区块史中,如果这一断言是错误的,我愿意损失V_LOSS,假设在所有区块史中,这一断言是正确的,我将得到V_GAIN。”验证者选择odds参数,而V_LOSS和V_GAIN,按照如下方式计算(令total_validating_ether为活动验证者集合的以太总量,MAX_REWARD是允许的区块奖励的最大值,bet_coeff是下文定义的系数):
- BASE_REWARD = FINALITY_REWARD_COEFFICIENT * BLOCK_TIME * total_validating_ether
- V_LOSS = BASE_REWARD * odds * bet_coeff
- V_GAIN = BASE_REWARD * log(odds) * bet_coeff
- BASE_REWARD = FINALITY_REWARD_COEFFICIENT * BLOCK_TIME * total_validating_ether
- V_LOSS = BASE_REWARD * odds * bet_coeff
- V_GAIN = BASE_REWARD * log(odds) * bet_coeff
![以太坊紫皮书(中文版)](http://8btc.com/data/attachment/forum/201609/25/231601l0a75o4nw30us5oz.png)
V_GAIN and V_LOSS values relative to the BASE_REWARD, assuming bet_coeff = 1,假设bet_coeff = 1,V_GAIN值和V_LOSS值与BASE_REWARD的相关性
FINALIZATION_TARGET从null开始, 但是区块1期间,它被设置成区块0,。bet_coeff初始(开始)值被设置为1,另一个变量cached_bet_coeff设置为0。但是bet_coeff不能减少到低于MIN_BET_COEFF,在每个区块,我们设置bet_coeff -= bet_coeff / FINALITY_REWARD_DECAY_FACTOR,cached_bet_coeff -= cached_bet_coeff / FINALITY_REWARD_DECAY_FACTOR(这确保了总有激励驱动去打赌)。当创建一个区块,验证者得到BASE_REWARD * log(MAXODDS) * cached_bet_coeff,其中MAXODDS是最大的可能赔率,如MAX_DEPOSIT_SIZE / BASE_REWARD。该机制随所实现的是,一旦某一区块被确定被敲定,验证者从中得到奖励,就像他们以最大odds继续进行验证一样。这确保了,验证者不会受到不正当的激励而共谋通过延迟敲定某个区块以获得最大收益。
当Casper合约确定FINALIZATION_TARGET已被敲定(即已知区块的总价值损失超过某个阈值),我们将新的FINALIZATION_TARGET设置成当前区块,我们设置cached_bet_coeff += bet_coeff,并且bet_coeff重新设置为1。从下一个区块开始,敲定过程重新开始设置新的FINALIZATION_TARGET。如果,有一个短链分裂,敲定过程可能同时处理多个区块,甚至是不同高度的区块;但是,鉴于区块投注的默认验证者策略,以及支持它的最高价值损失,我们预计收敛过程倾向于选择他们其中的一个(此处的收敛参数基本上与最小权益证明相一致)。
当新的区块敲定过程开始启动时,我们期望最初的赔率很低,这表示验证者对短范围分叉的恐惧,但是随着时间的推移,验证者愿意投注的赔率会逐渐增加。特别的如果他们看到其他验证者对该区块进行高赔率投注,验证者的赌注也会增加。可以预计,该区块的价值损失将以指数倍增加,从而在对数时间内,达到最大“总保证金损失”。
在区块标头的额外数据中,我们现在将所需的数据格式更改为以下格式:
<vchash><randao><blockhash><logodds><sig>
其中区块哈希是上个打赌的区块的哈希,并且logodds为1字节值,代表对数形式的赔率(即,0对应1,8对应2,16对应4等)。
请注意,我们不能允许验证者完全自由地设置赔率。考虑如下场景,如果有两个竞争关系的敲定目标,B1和B2(即存在两个链,其中一个的FINALIZATION_TARGET设置为B1,另一个的的FINALIZATION_TARGET设置为B2),并且围绕B1开始形成共识,然后一个恶意验证者可能会突然对B2投入一个高赔率赌注,它的价值损失足够影响共识,从而引发短范围分叉。因此,我们使用以下规则,通过限制V_LOSS来限制赔率:
- 令V_LOSS_EMA为一个指数移动平均线,设置如下。V_LOSS_EMA开始相当于区块奖励。对于每个区块周期,V_LOSS_EMA设置为V_LOSS_EMA * (V_LOSS_MAXGROWTH_FACTOR - 1 - SKIPS) / V_LOSS_MAXGROWTH_FACTOR + V_LOSS,其中SKIPS是跳过的数量,V_LOSS是该区块选择的V_LOSS。
- 设置V_LOSS_MAX为V_LOSS_EMA * 1.5。限定V_LOSS的值为该值。
该规则设计上引入了一个安全约束:当至少(样本)三分之二的其它验证者风险为x后,当前验证者的风险仅可以为1.5x。这与拜占庭容错共识算法的事先承诺或承诺模式类似,一个验证者应等待其他三分之二的验证者完成给定的步骤后,才能进行下一步,并确保一定的安全,也确保大多数共谋不能参与“恶意破坏”攻击(如,让其他的验证者讲大量赌注添加在一个区块,并且随后推动共识到一个别的区块),无法共谋,因为共谋本身需要耗费大量资金(实际上,共谋损失金钱的速度比受害者损失金钱的速度更快,这是一个显著的特性,因为它确保,在大多数敌对情况下,恶意操作者会随着时间的推移,经常会被“淘汰”。)
如果一个区块作为一个dunkle被加入链中,赌注被处理,并且罚款和奖励也会产生。例如,如果有两个高度为5000的区块A1和A2,互为竞争的敲定目标,并且有两个高度为5050的区块B1和B2(两区块均以A1为上一代区块),并且验证者在B1上构建区块C,对A1下赌注,随后,如果B2最终确定为主链区块,B1和C将成为dunkle,并且C也将因B1 vs B2下错注而被惩罚,但是还是会因A1被赌对而进行奖励。
然而,假设C中的V_LOSS是这样的,如果B1包含在内,则V_LOSS < V_LOSS_MAX;如果B2包含在内,则V_LOSS > V_LOSS_MAX。之后,为了保证预期的价值损失特性,我们制定了一个额外的罚款:即使验证者赌对了,但我们仍以V_LOSS - V_LOSS_MAX对他们进行处罚。因此,我们有效地将V_LOSS赌注,分解成(i)价值损失V_LOSS_MAX的赌注和(ii)V_LOSS - V_LOSS_MAX的单纯价值销毁,从而保证这种规模过大的赌注仍仅通过V_LOSS_MAX经由分叉选择规则变换。这意味着投注在某种意义上是不“完全的”,因为如果该区块的子代区块中的很多都分叉了,即使该区块已经完成敲定确定,某个区块的赌注仍可能引起罚款。在投注模型中的单纯价值损失,被认为是对价值损失分叉选择规则利润纯度的一个可接受的权衡。
计分与策略实现
价值损失计分可以通过以下算法实现:
- 保持追踪最新的敲定的区块。如果有多个敲定的区块不兼容,返回一个大的红色的闪烁错误,因为这表明一个敲定的逆转事件发生了,并且客户端的用户可能需要使用额外的链源确定发生了什么。
- 保持追踪所有敲定的候选区块,即该区块的子代区块。对于每一个候选区块,保持追踪该候选区块的价值损失。
- 从最接近的敲定区块开始,保持追踪每个敲定候选区块的最长链及其长度。
- 链的“总权重”是其敲定的候选区块祖先的价值损失加上链的长度乘以区块奖励。如果链中没有敲定的候选区块,那么就单独使用链的长度乘以区块奖励作为其总权重。“链头”是链中权重最大的最新区块。
![以太坊紫皮书(中文版)](http://8btc.com/data/attachment/forum/201609/25/231741sxmo697ooo9qooqq.jpg)
此处用于举例的V_LOSS;在现实中,他们不会被允许这样快速的增长,并且B或C会要求A上的较高的V_LOSS成为敲定候选区块。
一个简单的验证者策略是仅在头部创建区块,并且做出敲定赌注,此处的价值损失是所描述的最大价值损失的80%。
轻客户端同步
敲定机制开启了一个快速轻客户端同步算法的大门。该算法包括以下几步:
![以太坊紫皮书(中文版)](http://8btc.com/data/attachment/forum/201609/25/231825fb0xjjmpjbfrr33f.png)
- 令X是您已经确认的最新状态(初始为创世状态)。
- 在X时期或X时期后查询网络当前最新的敲定目标(谨记:当协议认为当前区块的前一区块被敲定时,此时的敲定目标为该区块。)。调用最新的敲定目标Fn和以前的敲定目标Fp。
- 查询网络Fn前的k个区块。这些区块将下注,筹码是它们在Fp上的整个以太币池子。
- 通过Merkle分支查询您已经敲定的状态,验证验证者创建的这些区块,验证其在验证者集合中的的存在性和位置,并且针对k个区块的中第一个区块的初始状态,验证他们选择的正确性。
- 设置X为Fp的后状态。
- 重复上述步骤,直到得到最新的敲定区块。从最新的敲定区块开始,使用上文的正常策略寻找链头。
非常值得关注的是,通过步骤1到5可以用两个网络请求和几秒钟计算来验证全天的区块。
分片
现在,我们考虑从一个分片扩大到多个分片。我们构建的模型如下。取代已有的单条区块链,我们现在拥有被我们称为“分片”的多条相关联的区块链。NUM_SHARDS分片的编号为分片0至分片NUM_SHARDS – 1,其中分片0简单的作为常规股权证明区块链进行,其确定性如上所述,但是分片1……NUM_SHARDS – 1工作机制有所不同。在每个时期的开始,为每个分片随机挑选VALIDATORS_PER_SHARD个验证者,并且被分配为下一个时期的验证者(如,n+1时期的验证者在n时期时被指配)。当调用getValidator(skip)以确定这些分片内一个分片的验证者,仅仅随机从选取的验证者集合中选择一个验证者(平等地分配,因为在挑选的时候就已经对保证金大小进行加权)。分片1……NUM_SHARDS – 1的确定性赌注并未在分片内做出,而是在分片0内做出。当赌注被做出,它就被存储,赌注仅在子代区块周期结束后,才被处理(如,n+1时期的区块确定断言,将在n+3区块周期开始时,在分片0内进行处理)。
![以太坊紫皮书(中文版)](http://8btc.com/data/attachment/forum/201609/25/231940ov59870ma0em8606.jpg)
如果已经为一个分片挑选了一个验证者,那么这个验证者将需要调用Casper合约的registerForShard(bytes32 vchash,uint256 shard,uint256 index,bytes32 randao)函数,其中vchash是验证者的验证代码哈希,shard是分片ID,index是一个数值且0 <= index < VALIDATORS_PER_SHARD,其中得getShardValidator(uint256 shard,uint256 index)返回给定的验证代码哈希,并且randao是一个randao承诺。为了引导验证者,该函数生成了一个收据,该收据能够通过利用confirmReceipt(uint256 receiptId)在目标分片上进行确认。
getShardValidator依赖于一个单独的随机源,业务逻辑与getValidator类似。该随机源,按照如下步骤得出:
- 每个时期,每个k,如果k满足 0 <= k < 24,保持追踪globalRandao最后第k的bit为1的次数合计,减去第k bit为0的次数合计。
- 每个时期结束,计算combinedRandao如下。每个k,如果k满足0 <= k < 24,并且如果在时期内,globalRandao的最后第k bit更多次是1,combinedRandao第k最后的bit是1。如果globalRandao的最后第k bit更多次是0,combinedRandao第k的bit是0.利用sha3(combinedRandao)作为随机源。
使用Iddo Bentov的低影响函数,增加了随机源的操纵成本,因为,这个特别的随机源种子会产生实质性的经济结果,因此,它比正常操纵目标更大一些。
跨分片敲定赌注并不在区块标头上,所以不会过度的阻碍轻客户端;相反,验证者将创建一个交易,在它们创建的任何区块间,调用一个registerFinalityBets(bytes32[] hashes,bytes logodds)函数,任意一个区块中创建预期的NUM_SHARDS个哈希和一个长度为NUM_SHARDS的字节数组,每个字节代表相应的区块哈希的赔率。
验证者的典型工作流程是维持分片0的一个“全节点”,并且保持追踪被分配给它们的未来分片。如果一个验证者被分配给一个分片,他们将利用Merkle树证明下载状态,并且确保当他们需要开始验证时,他们已经下载了相应状态。对于该时期,他们作为该分片的验证者,并且创建区块,同时,他们将在所有的分片上做出敲定赌注,他们通过观测(i)每个分片上的最长链,(ii)其他的验证者的敲定赌注,以及(iii)在片区内试图达到51%成功攻击的各种二次启发方法和机制(如,欺诈证明)。注意,分配到任何给定分片的概率与验证者的累计以太成比例,因此,如果验证者的资产成倍,那么需要处理的计算也会加倍。这个特性被认为是何意的,因为它增加了公平性,并降低了矿池激励,同时引入了一个原则,该原则下处理交易并存储区块链本身也成为了“工作量混合证明”的一种形式。
取样机制的初衷是为了确保系统仅依靠少量验证者进行实际交易验证,同时系统能够安全应对累积以太保证金高达~33-40%(低于50%,因为总累积以太为33-50%的攻击者在某些给定的分片会很“幸运”)的攻击者;因为取样是随机的,攻击者不能够选择将他们的权益集中在一个分片上,许多工作量分片方案的一个致命缺陷上。即使一个分片被攻击了,还有第二道防线:如果其他验证者发现攻击的证据,那么他们可以拒绝做出跟随攻击者分叉的敲定声明,而确认由诚实节点创建的链。如果一个分片上的一个攻击者试图从无效区块创建一个链,其他分片的验证者可以检测到,并且之后暂时完全验证该分片上的节点,并且确保他们仅敲定有效的区块。
跨分片通信
本方案中的跨分片通信遵循如下工作。我们创建一个ETHLOG操作码(带有两个参数:to value),其创建的一个日志记录,其存储内容是空字符串(注意,该空字符串,不是32个零字节,一个传统的LOG只能存储32字节的字符串),它的数据是一个64字节的字符串,包括目标和具体值。我们创建的GETLOG操作码,需要一个由区块定义ID的单独的参数。number * 2**64 + txindex * 2**32 + logindex(其中txindex是交易的index,包括区块的log,logindex是交易receipt中的log index)试图获取指定的存储内容,存储在状态中的日志表明,该日志已被消耗,并且将日志数据放在目标数组内。如果日志值为空串,这也将传输以太给收件人。为了成功得到日志内容,调用操作码的交易必须携带日志ID参数。如果v=0,我们允许签名中的r值重新用于此目的(注意:这意味着此处仅可以使用EIP 86交易;我们希望到现在为止,EIP 86交易将是交易的主要形式)。
现在抽象共识再也不是一个单链,而是一个链的集合,c[0]…c[NUM_SHARDS - 1]。状态转移函数不再是stf(state, block) -> state',而是stf(state_k, block, r_c[0]…r_c[NUM_SHARDS - 1]) ->state_k,其中
- r_c[i]
请注意,有几种方法可以“满足”这种抽象。一种方法是“每个节点都是全功能节点”的方法:所有节点存储所有分片的状态,更新所有分片的链,因此拥有足够的信息,可以计算所有的通道交易函数。然而,这是令人乏味的,因为它是不可扩展的。
更有趣的策略是“中型节点”方法:大多数节点选择一些分片,保持更新到最新(尽可能包括分片0),并作为所有其他分片的轻客户端。当计算通道交易函数时,他们需要旧交易收据,然而,他们并没有存储旧交易收据,为此,我们增加了一个网络协议规则,要求交易伴随着任何交易静态参考的收据的Merkle证明(这里为什么需要静态参考就很清楚了:否则,在运行时间创建的任意一个GETLOG操作,因为网络延迟,日志读取数据将变成一个缓慢的过程,将耗费数倍时间,并且客户存储本地的所有历史日志的负担太重)。最终,在主网络内部署的策略将有可能是带有初始强制receipt Merkle证明的全节点策略,随着时间的推移,松散支持越来越多的中型节点。
注意,不需要将Merkle证明作为一个数据包,从一个分片直接导入另一个分片,反而是,所有的证明传输逻辑均在验证者和客户端层面处完成,并且在协议层面实现了一个接口,通过该接口可以访问Merkle。长时间ASYNC_DELAY减少了一个分片内改组的可能性,这将需要整个通道的集中改组。
如果需要短时间延迟,可以在协议之上实施的一种机制是分片内投注市场,例如,在分片j内,A可以和B打一个赌,比如说“如果在分片i内,区块X被敲定,B同意向A发送0.001 ETH,反之,如果在分片i内,区块X未被敲定,则A同意向B发送1000 ETH”。针对此目的,Casper保证金可以有双重用途-即使在分片j内进行打赌,A赌输的信息将通过receipt传输给分片0,随后一旦A提取,分片0将转移1000以太给B。B将因此获信:A足够确信其他分片的区块将被敲定并据此而做出投注,同时B也获得了防止A判断失败的一种保险(即使运用了双重用途方案,该保险还是有缺陷的,如果A是恶意的,那么他们将输掉他们全部的赌注,这样B将什么也得不到)。该方案存在的一个可扩展限制,与一个节点计算能力的平方成比例。原因有二,首先,与分片数量成比例的计算能力总数必须在分片0上计算奖励。其次,所有客户端必须为所有分片的轻客户端。因此,假设节点的计算能力为N,则应该存在O(N)个分片并且每个分片的处理能力均为O(N),其网络总处理能力为O(N^2)。超过这个最大值,将需要一种更加复杂的分片协议以构成某种树结构的声明验证,这超出了本文的范围。
译者&来源:LinktimeTech 微信公众号
在刚刚闭幕的以太坊Devcon 2大会上,以太坊创始人Vitalik分享了他的最新研究成果《以太坊紫皮书》,在此我们将其翻译成中文,供大家阅读。《紫皮书英文》原版大家也可以从我们的公众号中找到并阅读。当然,如此前沿和复杂的论文翻译是很大的考验,如果大家觉得以下内容有问题,请及时与我们反馈,我们将在后期的公众号文章中进行纠正。论文全文内容如下:
![以太坊紫皮书(中文版)](http://8btc.com/data/attachment/forum/201609/25/231030scmovsvno9n0bnfv.jpg)
(为什么叫紫皮书?)
前言
在过去十年中,诸如Bitcoin、Namecoin和以太坊这些项目充分展现了密码经济共识网络对促成下一代去中心化系统的强大推力,并潜移默化的将开发视野从简单的数据存储和信息服务扩展到任何状态应用的后台管理。基于这个系统,在全球范围内提议和执行的应用,涵盖了全球低价支付系统,金融合同,市场预测,身份注册和现实世界产权,建立更安全的证书管理系统,甚至通过供应链对制造品进行溯源和跟踪管理。
但是,这个系统的技术基础仍然存在严重的效率问题。因为在网络上每个全节点都必须维护整个系统的状态和处理每个交易,整个区块网络效率受限于单个计算节点。现在大部分系统所采用的共识机制,工作量证明(POW),需要消耗大量的电力去运营; 基于POW机制的最大的工作区块链Bitcoin,消耗了相当于整个爱尔兰的用电量。
这篇文章为上述问题提供将POS和基于分片证明进行合并的解决方案。POS本身并不是一个新奇的主意,2011年就已经存在,但新的算法展现了实质性的好处,不仅解决了前一系统的缺陷,甚至拥有POW不曾有的属性。
POS可以被想象成一种虚拟挖矿。然而在POW模式下,用户需要花费一定的金钱买一台电脑,然后消耗真实的电力,其随机获得区块链的成本与消耗的电力大致成比例。在POS模式下,用户花费金钱购买系统内的虚拟代币,然后用一个内部协议机制将虚拟代币转换成虚拟电脑,系统模拟随机产生的区块的成本与购买成本大致成比例,达到了POW同样的出块效果,却不用消耗电力。
分片也并不新颖,在现行的分布式数据库设计中有超过10年的应用,但是到目前为止,研究将其应用在区块链上,仍有颇多限制。基本的路径是解决可扩展的挑战, 通过架构中的全球验证程序集合中的节点(在我们的情况下,通过股权结合证明了)被随机分配到特定的“碎片”,其中每个碎片并行处理全局状态的不同部分,从而确保工作是跨节点分布处理,而不是每个节点都重复做。
我们渴望实现以下目标
- 通过POS提升效率:共识机制不应该由挖矿进行保证,从而大大减少电力浪费,并且可以满足大量和持续发行ETH的需要。
- 快速的出块时间:在不威胁安全的前提下,出块速度达到最大值。
- 经济一致:一旦区块被制作,经过一定时间和事件的处理,大部分的验证者将‘全提交’那个区块,意味着他们将损失全部的以太币保证金在没有包含这个区块的历史记录(想想1000万价值的以太币),这是非常需要的,因为意味着大部分的碰撞不能通过节点进行传递或者不用破坏掉以太币就可以51%攻击。默认的验证者战略是被设计成保守的他们愿意做出高价值的承诺,诚信的验证者的风险应该很低。
- 可扩展性:应该不需要跑所有节点就可以运行区块链,例如在这样的情况下,所有节点包括验证节点只保持一小部分区块的数据碎片,然后用轻客户端技术访问剩余部分的区块。采用方式下,相对单个节点处理能力,区块链可以达到更高的交易处理吞吐量,同时运行区块平台所需的只是大量的普通个人电脑,因此也可以保证去中心化。
- 跨碎片通信:构建这样一类应用并实现应用之间的互操作性在理论上是具有最大可行性的。该类应用的资源使用达到一个临界点以至超出单个节点的计算能力和带宽限制,并且分别存储在不同节点和处于不同状态。
- 抵抗计算审查:该协议设计为可抵抗大部分的恶意验证节点跨越所有碎片而发起的联合攻击,使得无效交易不能被打包到区块并成为整个区块链的一部分。在某种程度上,可以通过以太坊1.0的停机问题存在,但是我们可以通过引入保证调度的概念和保证交叉碎片信息使这个机制更加强健。
我们从描述一个算法开始,该算法实现目标1和2, 然后在第二个算法实现了目标3, 然后在第三个算法中一定程度上实现目标4,5(作为一个限制条件,对一个节点的计算能力的平方大致成比例,如(4)和一个24小时的延迟跨碎片信息,有可能建立更快的消息作为一个层上通过双用途的存款,在例(5)。对目标(4)和(5)的更强级别的满意,(6)也一样,使得重新设计2.1和3.0。
常数
我们设置:
- BLOCK_TIME: 4 seconds (aiming on the less ambitious side to reduce overhead,针对不太有野心的目标去减少开销)
- SKIP_TIME: 8 seconds (aiming on the less ambitious side to reduce overhead针对不太有野心的目标去减少开销)
- EPOCH_LENGTH: 10800 (ie. 12 hours under good circumstances,12小时的良好状态)
- ASYNC_DELAY: 10800 (ie. 12 hours under good circumstances12小时的良好状态)
- CASPER_ADDRESS: 255
- WITHDRAWAL_DELAY: 10000000, ie. 4 months
- GENESIS_TIME: some future timestamp marking the start of the blockchain, say 1500000000
- REWARD_COEFFICIENT: 3 / 1000000000
- MIN_DEPOSIT_SIZE: 32 ether
- MAX_DEPOSIT_SIZE: 131072 ether
- V_LOSS_MAXGROWTH_FACTOR: 32
- FINALITY_REWARD_COEFFICIENT: 0.6 / 1000000000
- FINALITY_REWARD_DECAY_FACTOR: 1000 (ie. 1.1 hours under good circumstances)
- MIN_BET_COEFF: 0.25
- NUM_SHARDS: 80
- VALIDATORS_PER_SHARD: 120
最小的POS
(注意,后续内容假设读者有对以太坊1.0的基本了解)
我们可以创建一个最小可行的PoS算法,没有诸如敲定确定、额外的抗审查以及分片等特性。在地址CASPER_ADDRESS存在一个合约,其主要功能是追踪验证者集合的变化。该合约没有特殊的特权,除了调用该合约是验证区块标头过程中的一部分,而且是包括在创世区块,而不是通过交易被动态添加的。验证者集合初始设置在创世区块中,并可以通过以下函数进行调整:
- deposit(bytes validation_code, bytes32 randao, address withdrawal_address): 保证金(字节validation_code,bytes32 randao,地址withdrawal_address)
接受一定量的以太币作为保证金,发送者指定一段验证代码(本质为EVM字节码,代码主要功能是用作一种公钥,以便后续其他的节点验证由他们进行签名的区块数据以及相关的网络共识消息),一个随机提交的交易(一个32字节的哈希用于验证者的选择;详见下文)和最终提现地址一并发送。值得注意的是,提现可以在一个特定的地址发送,该地址的合约功能唯一用途就是特定的地址条件下释放资金,如果需要还可以双重用保证金。如果所有参数都被接受,在下一个时期将增加该验证者到验证者集合。(例如,如果保证金在第N个时期请求提取, 而验证者在第N+2个时期被加入验证者集合,这个一个时期等于EPOCH_LENGTH个区块时间周期)。验证代码的哈希值(叫做vchash)可以被验证者用作识别号;不同验证者具有同样的哈希值是被禁止的。)
startWithdrawal(bytes32 vchash, bytes sig):
开始撤回流程,要求一个签名,该签名需要通过验证者的验证代码。如果签名通过,从下一个时期开始,验证者被从验证者集撤回。注意,这个函数不退回以太币。
还有一个函数:
withdraw(bytes32 vchash):
撤回验证者的以太币到指定的提现地址,增加奖励减少惩罚,只要验证者已经从验证者活动集合中通过使用 StartWithdrawal至少是WITHDRAWAL_DELAY几秒前撤回。
准确的讲,验证代码就像是放入区块标头的哈希码,加上签名,如果签名有效返回1,反之,则返回0。这个机制确保了我们不会将验证者锁定到任何一个特殊签名的算法,相反允许验证者使用验证代码从多重私钥验证签名取代单一验证,也允许使用Lamport签名对抗量子计算机的攻击。这个代码在黑匣子环境下通过使用新的CALL_BLACKBOX操作码执行,以保证执行独立于外部状态。这样可以防止一些攻击,如一个验证者创建了验证代码在状态良好的时候返回1,在状况不好的时候(例如 Dunkle inclusion)返回0.
deposit函数中randao 参数的值应该是计算一段长链哈希值结果,即设置秘密随机的X,执行计算randao = sha3(sha3(sha3(sha3(.....(sha3(x))…))。每个验证者提供randao的值保存在Casper 合约中的存储空间。
Casper合约也含有一个叫GlobalRandao的变量,初始化为0。这个合约含有一个函数getValidator(uint256 skips):返回 skips 跳过以后的验证者的代码。例如 getValidator(0)返回第一个验证者(验证者一般来讲可以创造区块),getValidator(1)返回第二个验证者(如果第一个不能创建区块,验证者可以创建)
每个验证者都是通过伪随机算法从目前活动的验证者集合进行选择,随机对初始保证金规模进行加权,并以Casper 合约中的globalRandao值为伪随机种子。除了签名外,一个有效的区块也必须包含为那个验证者目前保存Randao的原像。这个原像然后替换保存Randao值,然后也通过异或运算保存到合约的globalRandao值中。这样,一个验证者生成的每一区块都要求脱掉一个验证者的randao的一层。这是一种基于在这里解释( http://vitalik.ca/files/randomness.html )的随机算法。
总之,一个区块必须包含的如下的额外数据:
<vchash><randao><sig>
其中,vchash是验证代码的32字节哈希值,用于快速识别验证者。randao含义如上所描述(也是32字节),sig是签名,可以是任意长度(虽然我们将区块标头的大小限制为2048字节)。
![以太坊紫皮书(中文版)](http://8btc.com/data/attachment/forum/201609/25/231434sg2fr36rzzi22z4q.png)
创建区块所需要的最短时间可以简单的定义为: GENESIS_TIME + BLOCK_TIME * <block height> + SKIP_TIME * <自创世区块后所有的验证者跳数和>。
在实际过程中,这意味着,一旦发布了某个区块,那么下一个区块的0-skip验证者会在BLOCK_TIME秒之后发布,同理,1-skip验证者则在BLOCK_TIME + SKIP_TIME秒之后发布,以此类推。
如果一个验证者发布一个区块太早,其他的验证者会忽视该区块,直到在规定的时间之后,才会处理该区块(该中机制的进一步描述和验证详见这里( http://vitalik.ca/files/timing.html);短BLOCK_TIME和长SKIP_TIME之间的不对称性,可以确保:在正常情况下,区块的平均保留时间可以非常短,而在网络延迟更长的情况下,也可以保证网络的安全性)。
如果一个验证者创建了一个包括在链内的区块,他们得到的区块奖励相当于此周期内活动验证者集合内以太的总量乘以REWARD_COEFFICIENT * BLOCK_TIME。因此,如果验证者总是正确的履行职责,REWARD_COEFFICIENT本质上成为验证者的“预期每秒收益率”;乘以~3200万得到近似的年化收益率。如果一个验证者创建了一个不包括在链内的区块,之后,在将来的任意时间(直到验证者调用withdraw函数为止)该区块标头可以作为一个“dunkle”通过Casper合约的includeDunkle函数,被包括在链内;这使得验证者损失了相当于区块奖励的钱数(以及向包括dunkle在内的当事方提供一小部分罚款作为激励)。因此,验证者应当在确定该区块在链内的可能性超过50%,才实际创建该区块;该机制不鼓励所有链上的验证。验证者的累计保证金,包括奖励和罚款,存储在Casper合约的状态内。
“dunkle”机制的目的是为了解决权益证明中的“零赌注”的问题,其中,如果没有罚款,只有奖励,那么验证者将被物质激励,试图在每个可能的链上创建区块。在工作量证明场景下,创建区块需要成本,并且只有在“主链”上创建区块才有利可图。Dunkle机制试图复制工作量证明中的经济理论,针对创建非主链上区块进行人工罚款,来代替工作量证明中电费用的“自然罚款”。
假设一个固定大小的验证者集合,我们可以很容易的定义分叉选择规则:计算区块数,最长链胜出。假设验证者集合可以变大和缩小,但是,该规则就不太适用了,因为作为少数支持的分叉的速度一个时期以后将像多数支持的分叉一样。因此,我们可以用计算的区块数代替定义的分叉选择规则,给每个区块一个相当于区块奖励的权重。因为区块奖励与积极验证的以太总量成正比,这确保了更积极验证以太的链得分增长速度更快。
我们可以看出,这条规则可以用另一种方式很方便的理解:基于价值损失的分叉选择模型。该原则是:我们选择验证者赌最多价值的链,就是说,验证者承认除了上述的链外,所有其他链都会损失大量的资金。我们可以等同认为,这条链是验证者失去资金最少的链。在这样一个简单的模式中,很容易看出这如何简单的对应着区块权重为区块奖励的最长链。该算法是尽管简单,但用于股权证明的实现来说也足够高效。
添加敲定
下一步是增加经济敲定的概念。我们按照以下方式进行。在区块标头内部,除了指向上一个区块的哈希之外,现在验证者也对某个以前区块FINALIZATION_TARGET敲定概率做出了一个断言。该断言被当做一个赌注,如“我相信区块0x5e81d……将被敲定,并且在所有区块史中,如果这一断言是错误的,我愿意损失V_LOSS,假设在所有区块史中,这一断言是正确的,我将得到V_GAIN。”验证者选择odds参数,而V_LOSS和V_GAIN,按照如下方式计算(令total_validating_ether为活动验证者集合的以太总量,MAX_REWARD是允许的区块奖励的最大值,bet_coeff是下文定义的系数):
- BASE_REWARD = FINALITY_REWARD_COEFFICIENT * BLOCK_TIME * total_validating_ether
- V_LOSS = BASE_REWARD * odds * bet_coeff
- V_GAIN = BASE_REWARD * log(odds) * bet_coeff
- BASE_REWARD = FINALITY_REWARD_COEFFICIENT * BLOCK_TIME * total_validating_ether
- V_LOSS = BASE_REWARD * odds * bet_coeff
- V_GAIN = BASE_REWARD * log(odds) * bet_coeff
![以太坊紫皮书(中文版)](http://8btc.com/data/attachment/forum/201609/25/231601l0a75o4nw30us5oz.png)
V_GAIN and V_LOSS values relative to the BASE_REWARD, assuming bet_coeff = 1,假设bet_coeff = 1,V_GAIN值和V_LOSS值与BASE_REWARD的相关性
FINALIZATION_TARGET从null开始, 但是区块1期间,它被设置成区块0,。bet_coeff初始(开始)值被设置为1,另一个变量cached_bet_coeff设置为0。但是bet_coeff不能减少到低于MIN_BET_COEFF,在每个区块,我们设置bet_coeff -= bet_coeff / FINALITY_REWARD_DECAY_FACTOR,cached_bet_coeff -= cached_bet_coeff / FINALITY_REWARD_DECAY_FACTOR(这确保了总有激励驱动去打赌)。当创建一个区块,验证者得到BASE_REWARD * log(MAXODDS) * cached_bet_coeff,其中MAXODDS是最大的可能赔率,如MAX_DEPOSIT_SIZE / BASE_REWARD。该机制随所实现的是,一旦某一区块被确定被敲定,验证者从中得到奖励,就像他们以最大odds继续进行验证一样。这确保了,验证者不会受到不正当的激励而共谋通过延迟敲定某个区块以获得最大收益。
当Casper合约确定FINALIZATION_TARGET已被敲定(即已知区块的总价值损失超过某个阈值),我们将新的FINALIZATION_TARGET设置成当前区块,我们设置cached_bet_coeff += bet_coeff,并且bet_coeff重新设置为1。从下一个区块开始,敲定过程重新开始设置新的FINALIZATION_TARGET。如果,有一个短链分裂,敲定过程可能同时处理多个区块,甚至是不同高度的区块;但是,鉴于区块投注的默认验证者策略,以及支持它的最高价值损失,我们预计收敛过程倾向于选择他们其中的一个(此处的收敛参数基本上与最小权益证明相一致)。
当新的区块敲定过程开始启动时,我们期望最初的赔率很低,这表示验证者对短范围分叉的恐惧,但是随着时间的推移,验证者愿意投注的赔率会逐渐增加。特别的如果他们看到其他验证者对该区块进行高赔率投注,验证者的赌注也会增加。可以预计,该区块的价值损失将以指数倍增加,从而在对数时间内,达到最大“总保证金损失”。
在区块标头的额外数据中,我们现在将所需的数据格式更改为以下格式:
<vchash><randao><blockhash><logodds><sig>
其中区块哈希是上个打赌的区块的哈希,并且logodds为1字节值,代表对数形式的赔率(即,0对应1,8对应2,16对应4等)。
请注意,我们不能允许验证者完全自由地设置赔率。考虑如下场景,如果有两个竞争关系的敲定目标,B1和B2(即存在两个链,其中一个的FINALIZATION_TARGET设置为B1,另一个的的FINALIZATION_TARGET设置为B2),并且围绕B1开始形成共识,然后一个恶意验证者可能会突然对B2投入一个高赔率赌注,它的价值损失足够影响共识,从而引发短范围分叉。因此,我们使用以下规则,通过限制V_LOSS来限制赔率:
- 令V_LOSS_EMA为一个指数移动平均线,设置如下。V_LOSS_EMA开始相当于区块奖励。对于每个区块周期,V_LOSS_EMA设置为V_LOSS_EMA * (V_LOSS_MAXGROWTH_FACTOR - 1 - SKIPS) / V_LOSS_MAXGROWTH_FACTOR + V_LOSS,其中SKIPS是跳过的数量,V_LOSS是该区块选择的V_LOSS。
- 设置V_LOSS_MAX为V_LOSS_EMA * 1.5。限定V_LOSS的值为该值。
该规则设计上引入了一个安全约束:当至少(样本)三分之二的其它验证者风险为x后,当前验证者的风险仅可以为1.5x。这与拜占庭容错共识算法的事先承诺或承诺模式类似,一个验证者应等待其他三分之二的验证者完成给定的步骤后,才能进行下一步,并确保一定的安全,也确保大多数共谋不能参与“恶意破坏”攻击(如,让其他的验证者讲大量赌注添加在一个区块,并且随后推动共识到一个别的区块),无法共谋,因为共谋本身需要耗费大量资金(实际上,共谋损失金钱的速度比受害者损失金钱的速度更快,这是一个显著的特性,因为它确保,在大多数敌对情况下,恶意操作者会随着时间的推移,经常会被“淘汰”。)
如果一个区块作为一个dunkle被加入链中,赌注被处理,并且罚款和奖励也会产生。例如,如果有两个高度为5000的区块A1和A2,互为竞争的敲定目标,并且有两个高度为5050的区块B1和B2(两区块均以A1为上一代区块),并且验证者在B1上构建区块C,对A1下赌注,随后,如果B2最终确定为主链区块,B1和C将成为dunkle,并且C也将因B1 vs B2下错注而被惩罚,但是还是会因A1被赌对而进行奖励。
然而,假设C中的V_LOSS是这样的,如果B1包含在内,则V_LOSS < V_LOSS_MAX;如果B2包含在内,则V_LOSS > V_LOSS_MAX。之后,为了保证预期的价值损失特性,我们制定了一个额外的罚款:即使验证者赌对了,但我们仍以V_LOSS - V_LOSS_MAX对他们进行处罚。因此,我们有效地将V_LOSS赌注,分解成(i)价值损失V_LOSS_MAX的赌注和(ii)V_LOSS - V_LOSS_MAX的单纯价值销毁,从而保证这种规模过大的赌注仍仅通过V_LOSS_MAX经由分叉选择规则变换。这意味着投注在某种意义上是不“完全的”,因为如果该区块的子代区块中的很多都分叉了,即使该区块已经完成敲定确定,某个区块的赌注仍可能引起罚款。在投注模型中的单纯价值损失,被认为是对价值损失分叉选择规则利润纯度的一个可接受的权衡。
计分与策略实现
价值损失计分可以通过以下算法实现:
- 保持追踪最新的敲定的区块。如果有多个敲定的区块不兼容,返回一个大的红色的闪烁错误,因为这表明一个敲定的逆转事件发生了,并且客户端的用户可能需要使用额外的链源确定发生了什么。
- 保持追踪所有敲定的候选区块,即该区块的子代区块。对于每一个候选区块,保持追踪该候选区块的价值损失。
- 从最接近的敲定区块开始,保持追踪每个敲定候选区块的最长链及其长度。
- 链的“总权重”是其敲定的候选区块祖先的价值损失加上链的长度乘以区块奖励。如果链中没有敲定的候选区块,那么就单独使用链的长度乘以区块奖励作为其总权重。“链头”是链中权重最大的最新区块。
![以太坊紫皮书(中文版)](http://8btc.com/data/attachment/forum/201609/25/231741sxmo697ooo9qooqq.jpg)
此处用于举例的V_LOSS;在现实中,他们不会被允许这样快速的增长,并且B或C会要求A上的较高的V_LOSS成为敲定候选区块。
一个简单的验证者策略是仅在头部创建区块,并且做出敲定赌注,此处的价值损失是所描述的最大价值损失的80%。
轻客户端同步
敲定机制开启了一个快速轻客户端同步算法的大门。该算法包括以下几步:
![以太坊紫皮书(中文版)](http://8btc.com/data/attachment/forum/201609/25/231825fb0xjjmpjbfrr33f.png)
- 令X是您已经确认的最新状态(初始为创世状态)。
- 在X时期或X时期后查询网络当前最新的敲定目标(谨记:当协议认为当前区块的前一区块被敲定时,此时的敲定目标为该区块。)。调用最新的敲定目标Fn和以前的敲定目标Fp。
- 查询网络Fn前的k个区块。这些区块将下注,筹码是它们在Fp上的整个以太币池子。
- 通过Merkle分支查询您已经敲定的状态,验证验证者创建的这些区块,验证其在验证者集合中的的存在性和位置,并且针对k个区块的中第一个区块的初始状态,验证他们选择的正确性。
- 设置X为Fp的后状态。
- 重复上述步骤,直到得到最新的敲定区块。从最新的敲定区块开始,使用上文的正常策略寻找链头。
非常值得关注的是,通过步骤1到5可以用两个网络请求和几秒钟计算来验证全天的区块。
分片
现在,我们考虑从一个分片扩大到多个分片。我们构建的模型如下。取代已有的单条区块链,我们现在拥有被我们称为“分片”的多条相关联的区块链。NUM_SHARDS分片的编号为分片0至分片NUM_SHARDS – 1,其中分片0简单的作为常规股权证明区块链进行,其确定性如上所述,但是分片1……NUM_SHARDS – 1工作机制有所不同。在每个时期的开始,为每个分片随机挑选VALIDATORS_PER_SHARD个验证者,并且被分配为下一个时期的验证者(如,n+1时期的验证者在n时期时被指配)。当调用getValidator(skip)以确定这些分片内一个分片的验证者,仅仅随机从选取的验证者集合中选择一个验证者(平等地分配,因为在挑选的时候就已经对保证金大小进行加权)。分片1……NUM_SHARDS – 1的确定性赌注并未在分片内做出,而是在分片0内做出。当赌注被做出,它就被存储,赌注仅在子代区块周期结束后,才被处理(如,n+1时期的区块确定断言,将在n+3区块周期开始时,在分片0内进行处理)。
![以太坊紫皮书(中文版)](http://8btc.com/data/attachment/forum/201609/25/231940ov59870ma0em8606.jpg)
如果已经为一个分片挑选了一个验证者,那么这个验证者将需要调用Casper合约的registerForShard(bytes32 vchash,uint256 shard,uint256 index,bytes32 randao)函数,其中vchash是验证者的验证代码哈希,shard是分片ID,index是一个数值且0 <= index < VALIDATORS_PER_SHARD,其中得getShardValidator(uint256 shard,uint256 index)返回给定的验证代码哈希,并且randao是一个randao承诺。为了引导验证者,该函数生成了一个收据,该收据能够通过利用confirmReceipt(uint256 receiptId)在目标分片上进行确认。
getShardValidator依赖于一个单独的随机源,业务逻辑与getValidator类似。该随机源,按照如下步骤得出:
- 每个时期,每个k,如果k满足 0 <= k < 24,保持追踪globalRandao最后第k的bit为1的次数合计,减去第k bit为0的次数合计。
- 每个时期结束,计算combinedRandao如下。每个k,如果k满足0 <= k < 24,并且如果在时期内,globalRandao的最后第k bit更多次是1,combinedRandao第k最后的bit是1。如果globalRandao的最后第k bit更多次是0,combinedRandao第k的bit是0.利用sha3(combinedRandao)作为随机源。
使用Iddo Bentov的低影响函数,增加了随机源的操纵成本,因为,这个特别的随机源种子会产生实质性的经济结果,因此,它比正常操纵目标更大一些。
跨分片敲定赌注并不在区块标头上,所以不会过度的阻碍轻客户端;相反,验证者将创建一个交易,在它们创建的任何区块间,调用一个registerFinalityBets(bytes32[] hashes,bytes logodds)函数,任意一个区块中创建预期的NUM_SHARDS个哈希和一个长度为NUM_SHARDS的字节数组,每个字节代表相应的区块哈希的赔率。
验证者的典型工作流程是维持分片0的一个“全节点”,并且保持追踪被分配给它们的未来分片。如果一个验证者被分配给一个分片,他们将利用Merkle树证明下载状态,并且确保当他们需要开始验证时,他们已经下载了相应状态。对于该时期,他们作为该分片的验证者,并且创建区块,同时,他们将在所有的分片上做出敲定赌注,他们通过观测(i)每个分片上的最长链,(ii)其他的验证者的敲定赌注,以及(iii)在片区内试图达到51%成功攻击的各种二次启发方法和机制(如,欺诈证明)。注意,分配到任何给定分片的概率与验证者的累计以太成比例,因此,如果验证者的资产成倍,那么需要处理的计算也会加倍。这个特性被认为是何意的,因为它增加了公平性,并降低了矿池激励,同时引入了一个原则,该原则下处理交易并存储区块链本身也成为了“工作量混合证明”的一种形式。
取样机制的初衷是为了确保系统仅依靠少量验证者进行实际交易验证,同时系统能够安全应对累积以太保证金高达~33-40%(低于50%,因为总累积以太为33-50%的攻击者在某些给定的分片会很“幸运”)的攻击者;因为取样是随机的,攻击者不能够选择将他们的权益集中在一个分片上,许多工作量分片方案的一个致命缺陷上。即使一个分片被攻击了,还有第二道防线:如果其他验证者发现攻击的证据,那么他们可以拒绝做出跟随攻击者分叉的敲定声明,而确认由诚实节点创建的链。如果一个分片上的一个攻击者试图从无效区块创建一个链,其他分片的验证者可以检测到,并且之后暂时完全验证该分片上的节点,并且确保他们仅敲定有效的区块。
跨分片通信
本方案中的跨分片通信遵循如下工作。我们创建一个ETHLOG操作码(带有两个参数:to value),其创建的一个日志记录,其存储内容是空字符串(注意,该空字符串,不是32个零字节,一个传统的LOG只能存储32字节的字符串),它的数据是一个64字节的字符串,包括目标和具体值。我们创建的GETLOG操作码,需要一个由区块定义ID的单独的参数。number * 2**64 + txindex * 2**32 + logindex(其中txindex是交易的index,包括区块的log,logindex是交易receipt中的log index)试图获取指定的存储内容,存储在状态中的日志表明,该日志已被消耗,并且将日志数据放在目标数组内。如果日志值为空串,这也将传输以太给收件人。为了成功得到日志内容,调用操作码的交易必须携带日志ID参数。如果v=0,我们允许签名中的r值重新用于此目的(注意:这意味着此处仅可以使用EIP 86交易;我们希望到现在为止,EIP 86交易将是交易的主要形式)。
现在抽象共识再也不是一个单链,而是一个链的集合,c[0]…c[NUM_SHARDS - 1]。状态转移函数不再是stf(state, block) -> state',而是stf(state_k, block, r_c[0]…r_c[NUM_SHARDS - 1]) ->state_k,其中
- r_c[i]
请注意,有几种方法可以“满足”这种抽象。一种方法是“每个节点都是全功能节点”的方法:所有节点存储所有分片的状态,更新所有分片的链,因此拥有足够的信息,可以计算所有的通道交易函数。然而,这是令人乏味的,因为它是不可扩展的。
更有趣的策略是“中型节点”方法:大多数节点选择一些分片,保持更新到最新(尽可能包括分片0),并作为所有其他分片的轻客户端。当计算通道交易函数时,他们需要旧交易收据,然而,他们并没有存储旧交易收据,为此,我们增加了一个网络协议规则,要求交易伴随着任何交易静态参考的收据的Merkle证明(这里为什么需要静态参考就很清楚了:否则,在运行时间创建的任意一个GETLOG操作,因为网络延迟,日志读取数据将变成一个缓慢的过程,将耗费数倍时间,并且客户存储本地的所有历史日志的负担太重)。最终,在主网络内部署的策略将有可能是带有初始强制receipt Merkle证明的全节点策略,随着时间的推移,松散支持越来越多的中型节点。
注意,不需要将Merkle证明作为一个数据包,从一个分片直接导入另一个分片,反而是,所有的证明传输逻辑均在验证者和客户端层面处完成,并且在协议层面实现了一个接口,通过该接口可以访问Merkle。长时间ASYNC_DELAY减少了一个分片内改组的可能性,这将需要整个通道的集中改组。
如果需要短时间延迟,可以在协议之上实施的一种机制是分片内投注市场,例如,在分片j内,A可以和B打一个赌,比如说“如果在分片i内,区块X被敲定,B同意向A发送0.001 ETH,反之,如果在分片i内,区块X未被敲定,则A同意向B发送1000 ETH”。针对此目的,Casper保证金可以有双重用途-即使在分片j内进行打赌,A赌输的信息将通过receipt传输给分片0,随后一旦A提取,分片0将转移1000以太给B。B将因此获信:A足够确信其他分片的区块将被敲定并据此而做出投注,同时B也获得了防止A判断失败的一种保险(即使运用了双重用途方案,该保险还是有缺陷的,如果A是恶意的,那么他们将输掉他们全部的赌注,这样B将什么也得不到)。该方案存在的一个可扩展限制,与一个节点计算能力的平方成比例。原因有二,首先,与分片数量成比例的计算能力总数必须在分片0上计算奖励。其次,所有客户端必须为所有分片的轻客户端。因此,假设节点的计算能力为N,则应该存在O(N)个分片并且每个分片的处理能力均为O(N),其网络总处理能力为O(N^2)。超过这个最大值,将需要一种更加复杂的分片协议以构成某种树结构的声明验证,这超出了本文的范围。