芯片
先前的章节介绍了一个低层的电路描述。在实现电路时, 我们通常会使用上层的API,这些API具有以可审核、高效、模块化和表达性强等特征。
在这些API中使用的一些术语和概念来源于集成电路的设计和布局。关于集成电路,通过组合特定功能的芯片更容易获得上述诸多特征。
举个例子, 我们可能有实现特定密码学原语的芯片,比如说哈希或者加密函数,或者像标量乘法或配对算法。
在UPA中,在实现了域上乘法和加法的标准门上可以构造任何电路逻辑。然而, 使用自定义门电路可以获得更高的效率。
采用我们的API, 我们可以设计使用自定义门的芯片。这些API是一个抽象层, 将上层的芯片设计和低层复杂的自定义门隔离开。
尽管有时我们需要 "身兼两职", 既要写上层的电路, 又要写上层电路所需要的芯片。这样做的目的是使代码更易于理解、审计和维护/重用。通过这种方法也排除了一些潜在的实现的错误。
UPA中的门通过相对引用 引用单元格,比如给定列中的某个单元格,或者某个选择子的相对偏移的单元格。当偏移非零时, 我们称之为 偏移引用 (也就是说, 偏移引用是相对引用的子集)。
和绝对引用 相反, 相对引用在相等约束中使用, 能指向任意单元格。
偏移引用的原因是减少配置中列的数量,从而减少证明的大小。如果没有偏移引用,则需要单独列来保存自定义门引用的每个值, 并我们需要使用相等约束,约束电路的其他单元格和该列中的单元格。使用偏移引用, 我们不仅需要更少的列, 我们也不需要为所有这些列增加约束,从而提高电路效率。
R1CS(对于一些读者来说,可能这个算术化更熟悉, 如果不是,也不要担心)电路包含了“海量的门”,这些门并没有语义上的顺序。 另一方面, UPA电路中的采用偏移引用, 行顺序非常重要。我们做一些简单的假定和定义一些抽象概念来控制电路结构复杂性: 小工具,内部实现电路构造,在小工具之间我们不采用相对引用或者特殊的门布局。
我们把一个电路分到多个区域 , 每一个区域都包含着一个不相交的单元格子集, 相对引用只在区域内使用。 芯片实现的部分职责是确保进行偏移引用的门被放置在区域的正确位置。
给定一些区域和他们的形状, 我们将使用一个单独的布局器 来决定每个区域放置在哪里(即起始行在哪里)。 目前实现了一个通用的布局器,如果有需要,你可以实现你自己的布局器。
布局器一般会在矩阵中留空一些区域, 因为在给定的行中,电路没有使用所有可用的列。 它们尽最大可能由不需要偏移引用的门填充。这些门可以被放置在一行中的任何位置。
芯片也定义了查找表。 如果同一个查找表论据中定义了多个表, 可以使用标记列指明每行使用了哪一个表。也可以把多个表组合成一张表(受多项式阶范围限制)进行查找。
芯片组合
为了将几个芯片的功能结合起来,我们将它们组合成树状。 最高层次的芯片定义fixed列、advice列和instance列,然后指定它们在较低层次芯片上的分布。
在最简单的情况下,每个底层芯片将使用与其他芯片不相同的列。然而,在芯片之间共享列也是允许的。优化advice列的数量尤其重要,因为这会影响证明大小。
芯片组合的结果(可能在优化之后)是一个UPA配置。电路实现将在芯片上参数化,并且可以通过上层芯片使用支持的底层芯片的功能。
我们希望不那么专业的用户通常能够找到支持他们需要的操作的现有芯片,或者只需要对现有芯片做微小修改。专家级用户可以自己实现电路优化。 ECC 以善于优化电路而闻名 🙂。