Sia的共识规则



  • 本文是对开发者文档Consensus.md的翻译,本人对Sia还属于初学阶段,翻译不周的地方欢迎各位给予指正!
    原文:https://github.com/NebulousLabs/Sia/blob/master/doc/Consensus.md

    版权声明:原创翻译发表于我的CSDN博客,转载请注明以下出处

    共识规则(Consensus Rules)

    本文档旨在从较高层次对Sia的加密体系进行良好的概述,并不会完全阐述所有的细枝末节。 对共识规则最精准的说明需要参照共识包*(译者注:consensus package,源代码存在于sia/modules下)*(以及所有依赖库)。

    如果您对区块链工作量证明(PoW: Proof of Work) 已有一般性的了解,而不是还需要尝试从最初级的原理开始着手的话,理解本文档的内容将会更容易一些。

    加密算法(Cryptographic Algorithms)

    Sia使用加密散列(cryptographic hashing)和加密签名(cryptographic signing),两者均有许多潜在的安全的算法可供选择使用。不可否认的是,我们对此缺乏足够经验,我们选择这些算法并非源于我们自身对这些算法特性的信心,而是因为其他人似乎对其抱有信心。

    • 对于散列算法,我们主要的关注点是使用的算法不能与比特币(Bitcoin)进行合并挖掘,哪怕只是一部分。次要的关注点是包括手机和其他低功率设备在内的消费者硬件上的散列速度。

    • 对于签名算法,我们的主要的关注点是签名校验的速度。次要的关注点是能够支持HD密钥。第三位的关注点是支持阈值签名。

    散列算法: blake2b

    选择blake2b作为散列算法是因为它是快速的,并且它的代码经过大量地审核,足够坚固到可以抵抗长度扩展攻击(length extension attack).

    blake2b的另一个特别重要的特征是,它不是SHA-2。 我们希望避免与比特币进行合并挖掘,因为这可能导致许多比特币矿工漠然地挖掘我们的区块链,从而使软分叉(soft fork)更加难以协调。

    签名算法: 可变类型签名(variable type signature)

    每个公钥均具有一个说明符(specifier)(一个16字节的数组)和包含公钥编码(encoding)的字节切片(byte slice)。 说明符用来指定在校验签名时使用哪个签名算法。 每个签名将是一个字节切片,其编码可以通过查看相应公钥的说明符来确定。

    这种方法可以轻松地将新的签名类型添加到货币(currency)中,且不会使现有的输出(output)和键(key)无效 。添加新的签名类型需要硬分叉(hard fork),但可以作为一种保护机制以防加密算法被破解的情况,并且如果在诸如校验速度、环签名(ring signature)等领域中有任何突破,也很容易移植到新的加密算法上。

    可选算法:

    • ed25519: 说明符必须匹配字符串“ed25519”。公钥必须被编码为32字节。签名和公钥将需要遵循ed25519规范。更多的信息可以在ed25519.cr.yp.to找到。

    • entropy: 说明符必须匹配字符串“entropy”。 签名将始终无效。 这提供了一种方式,可以将熵缓冲区(entropy buffer)添加到SpendCondition对象以保护低熵信息,同时又能够证明熵缓冲区是无效的公钥。
      (译者注:这一段好晕,需要进一步研究之后再加工)

    添加ECDSA secp256k1和Schnorr secp256k1算法也在计划之中。新的签名算法能够通过一个软分叉添加到Sia,因为无法识别的算法类型总是被认为具有有效的签名。

    货币(Currency)

    Sia加密体系下有两种类型的货币。第一种是云储币(siacoin)。云储币在每个区块中产生并分发给矿工们。然后,这些矿工可以使用获得的云储币为文件合约提供资金,或者可以将云储币转送给其他人。云储币由一个无限精度无符号整数所表示。

    Sia加密体系中的第二种货币是云储基金(siafund),它是一种限制在10,000个不可分割单位的特殊资产。每次进行文件合约支付(payout)时,支付金额的3.9%被放入云储基金池中。在云储基金池中的云储币的数量必须总是可以除以10,000,从文件合约支付中取出的云储币数量将向下舍入到最接近的10,000。云储基金也是由一个无限精度无符号整数所表示。

    云储基金所有者可以在云储基金池收集云储币。云储基金池中每增加10,000个云储币时,云储基金的所有者可以提取1个云储币。约8790个云储基金由Nebulous Inc.拥有,剩余的云储基金由Sia项目的早期支持者拥有。

    未来有计划使Sia兼容侧链(sidechain)。 这将允许其他货币,例如比特币,可以适用于云储币流通的所有相同的地方。

    数据编码(Marshalling)

    许多Sia的数据类型需要在某些场合进行散列,这就需要一个一致的算法,用于将这些类型的数据编码为可以被散列的字节数组。 散列规则如下:

    • 整数是小字节序(little-endian),总是被编码为8字节。

    • 布尔被编码为一个字节,其中0为假,1为真。

    • 诸如字符串的可变长度类型,前置8个字节的数据长度。

    • 数组和结构体中的元素各自被编码并连接在一起。结构体中元素的顺序由结构体定义确定。仅有一种方法来编码每个结构。

    • 货币类型(无限精度整数)编码为大字节序(big-endian),使用尽可能多的必要的字节来表示底层数字。由于它是一个可变长度类型,它以8个字节的数据长度为前缀。

    区块大小(Block Size)

    最大的区块大小为2*10^6(2e6)字节。交易(transaction)大小没有限制,但它必须能够被容纳于区块内部。大多数矿工强制每个交易的大小限制为16,000字节。

    区块时间戳(Block Timestamps)

    每个区块都有一个最小准许时间戳。它是通过取前面11个区块的中间时间戳来确定的。如果之前的区块不足11个,则需重复使用初始时间戳(genesis timestamp)。

    如果区块的时间戳超过未来3小时,这个区块将被拒绝。但经过足够长的时间之后,可以被重新接受。

    区块ID(Block ID)

    区块ID由以下规则生成:

    散列(父区块ID + 64位随机数+ 区块Merkle树根节点)

    通过创建Merkle树来获得区块Merkle树的根节点,Merkle树的叶子结点是时间戳的散列值、矿工输出(miner output)的散列值(每个矿工输出对应一个叶子结点)和交易(transaction)的散列值(每个交易对应一个叶子结点)。

    区块目标(Block Target)

    对于有效的区块,该区块ID必须低于某个特定目标。

    区块目标每隔500个区块被调整一次,通过查看前面1000个块的时间戳来进行调整。从最近的区块到之前第1000个区块之间所经过的预期时间间隔为10,000分钟。如果相比预期时间间隔已经过去了更多的时间,区块目标将被降低。如果比预期时间间隔少的话,则区块目标将被增加。每次调整可以将区块目标最大调整到2.5倍。

    区块目标与时间差成比例地改变(如果时间是预期的一半,新区块目标是旧区块目标的1/2)(译者注:原文好像有误,意思好像是说反了)。区块目标的调节有上下限。在一个区块中,区块目标不能向上调整超过1001/1000,并且不能向下调整超过999/1000。

    使用(以秒为单位的预期时间)/(以秒为单位实际时间)*(当前的区块目标)来计算新的区块目标。 除法和乘法应使用无限精度,计算结果应被截断(truncated)。

    如果不存在1000个区块,则使用初始时间戳(genesis timestamp)进行比较。
    预计时间为(10分钟*区块高度)。

    区块补贴(Block Subsidy)

    一个区块的币基数(coinbase)是(300,000 - 区块链高度)* 10 ^ 24,最小值为30,000 * 10 ^ 24。任何矿工酬劳(miner fee)都被添加到币基数中以创建区块补贴(block subsidy)。然后,区块补贴形成多个输出(output),称为矿工支出(miner payout)。矿工支出的总额必须等于区块补贴。

    由矿工支出创建的输出(output)的ID,以区块ID拼接该输出所对应的矿工支出的索引值(index)来确定。

    区块补贴生成的输出(output)不能在后续50个区块之内进行消费,并且在这50个区块已经产生之前,不被认为是共识集合的一部分。

    这种限制是适当的,因为简单的区块链重组足以使输出无效,从而使得进行双重消费攻击(double spend attack)和虚假消费攻击(false spend attack)变得更加容易的。

    交易(Transactions)

    一个交易(Transaction)主要由以下几个对象组成:

    • 云储币输入(Siacoin Inputs)
    • 云储币输出(Siacoin Outputs)
    • 文件合约(File Contracts)
    • 文件合约修订(File Contract Revisions)
    • 存储证明(Storage Proofs)
    • 云储基金输入(Siafund Inputs)
    • 云储基金输出(Siafund Outputs)
    • 矿工酬劳(Miner Fees)
    • 任意数据(Arbitrary Data)
    • 交易签名(Transaction Signatures)

    所有云储币输入(siacoin input)的总和必须等于所有矿工酬劳(miner fee)、云储币输出(siacoin output)和文件合约支出(file contract payout)的总和,没有剩余。所有云储基金输入(siafund input)的总和必须等于所有云储基金输出(siafund output)的总和。

    在上述的某几个对象中存在解锁散列值(unlock hash)。 解锁散列值是“解锁条件(unlock condition)”对象的Merkle树根节点。解锁条件包含一个时间锁(timelock)、若干必需的签名以及可在签名期间使用的一组公钥。

    解锁条件(unlock condition)对象的Merkle树根节点,是由时间锁(timelock)、公钥(每个公钥对应1个叶子节点)以及签名的数量所构成的Merkle树的根节点。 这种排序是特别选择的,因为时间锁(timelock)和签名的数量是低熵的。通过使用随机数据作为第一个和最后一个公钥,可以安全地暴露任何公钥,而不暴露低熵项。

    需要提供足够多的签名,并且区块链的高度至少等于时间锁(timelock)的值,才能满足解锁条件(unlock condition)。

    解锁条件(unlock condition)包含一组公钥,每个公钥只能在提供签名时使用一次。相同的公钥可以被包含两次,这意味着它可以使用两次。所需签名的数量指示必须使用多少公钥来验证输入(input)。如果所需的签名数量为“0”,则输入(input)是“任何人都可以消费”。如果所需的签名数量大于公钥的数量,则输入(input)是不可消费的。

    签名数量必须有足够多的。例如,如果存在3个公钥和仅2个所需的签名,则只有2个签名可以被包含在交易中。

    云储币输入(Siacoin Inputs)

    每个输入(input)消费一个输出(output)。所消费的输出(output)必须存在于共识集合(consensus set)中。输出(output)的"value"字段显示在交易(Transaction)的输出(output)中必须使用多少个云储币。有效的输出是矿工酬劳(miner fee)、云储币输出(siacoin output)和文件合约支出(file contract payout)。

    云储币输出(Siacoin Outputs)

    云储币输出(siacoin output) 包含一个币值和一个解锁散列值(unlock hash)(也称为币址)。解锁散列值是消费条件的Merkle树根节点,消费条件必须被满足才能消费此输出(output)。

    文件合约(File Contracts)

    文件合约是某方用以证明在某给定的时间点拥有某文件的一种协议。合约包含被存储数据的Merkle树根节点,以及被存储数据的字节大小。

    通过将文件分割成64字节的数据段,并对每个数据段进行散列以形成Merkle树的叶子结点,从而构成Merkle树根节点。最后一个数据段不补齐(padded out)(译者注:指64字节补齐)

    存储证明(storage proof)必须在合约中的“WindowStart”与“WindowEnd”字段所代表的区间内提交。合约中存在一个“支付(payout)”金额,它表示在提交存储证明时提供多少个云储币。该支付金额的3.9%(向下舍入到最接近的10,000)将被抽取给云储基金的所有者。如果存储证明(storage proof)被提供且有效,则剩余的支付金额被放入可由“有效证明消费散列(valid proof spend hash)”消费的输出(output)中;如果在“WindowEnd”之前没有向区块链提供有效的存储证明,则剩余的支付金额被放入可由“缺失证明消费散列(missed proof spend hash)”消费的输出(output)中。

    所有的合约都必须有一个非零的支付金额(payout),“WindowStart”必须在“WindowEnd”之前,'WindowStart'必须大于区块链的当前高度。在高度为“WindowEnd”的区块中提交存储证明是可以被接受的。

    文件合约创建时包含了一个“修订散列(Revision Hash)”,它是一个解锁条件(unlock condition)对象的Merkle树根节点。提交满足解锁条件对象的“文件合约修订”,使文件合约被替换成修订中所指定的新文件合约。

    文件合约修订(File Contract Revisions)

    文件合约修订可以修改一个文件合约。文件合约具有一个修订号,提交给区块链的任何修订必须具有更高的修订号才是有效的。除了支付(payout)字段之外,文件合约中的任何字段都可以更改,即:在修订中无法向文件合约中增加或减少云储币,虽然可以更改成功或不成功的存储证明的目标(destination ???)。

    文件合约修订的最大的应用场景是提供文件内容变更的一种渠道,即:当用户向托管主机(host)上传新的或不同的内容时,文件合约可以在区块链外(off-blockchain)被编辑许多次。这提高了Sia的整体可伸缩性。

    存储证明(Storage Proofs)

    一个存储证明交易是包含存储证明(storage proof)的任何交易。存储证明交易不允许有云储币或云储基金输出,并且不允许包含文件合约。

    当创建存储证明(storage proof)时,只需证明您有一个64字节的文件段。需要被证明的这一文件段使用文件合约id和'触发区块(trigger block)'的id来随机挑选。触发区块(trigger block)是高度为“Start”-1的区块,其中“Start”是履行此存储证明的文件合约中的字段“Start”的值。

    该文件由若干64字节的段组成,其散列构成Merkle树的叶子节点。当证明你拥有文件时,必须证明你有一个叶子节点。为了确定是哪个叶子节点,取文件合约id连接触发区块(trigger block)id的散列值,然后取其与段数量的模:

    Hash(文件合约id + 触发区块id) % 段数量

    存储证明是通过提供64字节的段形成,然后其余的散列值需要被填充直至填满整棵树。存储证明的总大小将是64字节 + 32字节 * log(段数量),并且可以由知道根散列值和文件大小的任何人来验证。

    存储证明交易是不允许有云储币输出、云储基金输出,或文件合约。由存储证明创建的所有输出不能在后续50个区块之内使用。

    这种限制是适当的,因为简单的区块链重组可以更改触发区块(trigger block),这将使存储证明无效,导致整个交易无效。这使得双重消费攻击(double spend attack)和虚假消费攻击(false spend attack)变得更加容易的。

    云储基金输入(Siafund Inputs)

    云储基金输入的工作原理类似于云储币输入。它包含将要消费的云储基金输出的id,以及消费此输出所需的解锁条件。

    当把云储基金输出用作输入时,将会创建一个特殊的输出。云储基金中自上次消费以来累积的所有云储币,都被发送到云储基金输出中的“索取消费散列(claim spend hash)”,这是一个正常的云储币地址。通过在创建输出时取出云储币池的大小并将其与云储币池的当前大小进行比较来确定云储币输出的值。方程是:

    ((当前云储币池大小 - 先前云储币池大小) / 10,000) * 云储基金数额

    像矿工输出和存储证明输出一样,一旦区块链重组,输出的值可能会改变,因此云储基金输出不能在后续50个区块之内使用。 然而,重组不会导致交易失效,因此不需要制定有关合约与输出的禁令。

    云储基金输出(Siafund Outputs)

    像云储币输出一样,云储基金输出包含一个值和一个解锁散列。该值表示被放到输出中的云储基金数额,并且解锁散列是输出被消费的解锁条件对象的Merkle树根节点。

    云储基金输出还包含一个索取解锁散列(claim unlock hash)字段,其指示在云储基金输出被消费时创建的云储币输出的解锁散列。所创建的输出的值将取决于在输出创建时点与消费时点之间的云储币池的增长。通过存储“索取开始(claim start)”来测量这种增长,该索取开始(claim start)表明在创建云储基金输出的时点的云储基金池的大小。

    矿工酬劳(Miner Fees)

    矿工酬劳是添加到区块补贴中的一定数量的云储币。

    任意数据(Arbitrary Data)

    任意数据是一组被共识集合忽略的数据。将来可以用于软分叉,与“任何人都可以消费”的交易相匹配。同时,它给第三方应用程序使用云储币区块链提供了一种简便的方法。

    交易签名(Transaction Signatures)

    每个签名指向单个解锁条件(unlock condition)对象中的单个公钥索引。 对于同一组解锁条件,不存在两个签名同时指向相同的公钥索引。

    每个签名包含一个时间锁(timelock),签名在区块链达到时间锁指定的高度之前是无效的。

    签名还包含一个“覆盖字段(covered fields)”对象,其指示交易的哪些部分包括在签名中。还有一个“全部交易(whole transaction)”标志,它指示除了签名之外,交易的每个部分都被包括在签名中,这消除了交易中签名之外的任何部分的可延展性。交易的签名字段也可以被单独包含,以强制指定此签名仅在某些其他签名存在时才是有效的。

    如果未设置全部交易(whole transaction)标志,则需要(向签名的涵盖字段(covered fields)对象中)手动添加所有相应的字段,并且其他方也可以添加新字段,这意味着交易将是可延展的。这允许其他方在您签署交易后添加额外的输入(input)、酬劳(fee)等,而不会使您的签名失效。如果设置了全部交易(whole transaction)”标志,则涵盖字段(covered fields)对象中,除签名字段外的所有其他元素必须为空。

    涵盖字段(covered fields)对象包含交易的每个元素的索引切片(云储币输入、矿工酬劳等)。切片必须排序,并且不能有重复的元素。

    完全不可延展的交易可以通过设置“全部交易(whole transaction)”标志,然后提供最后一个签名,包括签名中(译者注:应该是交易中???)的所有其他签名。由于不允许无意义的签名,在此签名无效前,交易无法被更改。

    (完)


Log in to reply