Solana 开发者指南 —— ZK 压缩技术

  • Helius
  • 发布于 2024-07-25 23:44
  • 阅读 19

文章详细介绍了Solana上的ZK Compression技术,讨论了其实现原理、信任假设、生命周期以及与传统ZK Rollup的区别。通过使用零知识证明,ZK Compression可以减少链上存储成本,同时保持Solana的同步原子组合性和并行性。

Solana Builders:ZK 压缩

17 分钟阅读

2024 年 7 月 23 日

Helius 和 Light Protocol 宣布他们在 Solana 上的零知识 (ZK) 压缩项目后的几天里,关于 ZK 压缩的讨论很多。大量的讨论集中在命名法上。它是 ZK rollup?L2?还是完全不同的东西?

为什么这很重要?Solana 生态系统中的一些人认为,关于命名的争论是多余的。我部分同意我们使用的名称并不像它所做的事情那么重要——但它仍然很重要,因为这些名称指代具有特定属性并将它们组合在一起的构造。因此,称之为 XYZ 可以告诉我们它的属性和信任假设——而这些我们应该关注!

属性

在我们在 ZK-压缩上下文中评估它们之前,让我们列出 Solana 一些我们感兴趣的属性:

  1. 同步原子组合性
  2. 并发性
  3. 安全性
  4. 活性
  5. 抵制审查

初始信任假设

我们将使用“无信任”一词来描述完整节点的安全假设。这个定义是我们的基准。任何完整节点无法单独完成的事情都涉及额外的信任假设。

旁注

完整节点是与协议进行交互的唯一无信任方式。如果有人试图引入额外的信任假设——桥接、委员会、多重签名、ZK、欺诈或其他任何东西,并声称它是无信任的,那么他们是错的。

一些背景

我会简要提供关于 Solana 的必要背景,以便理解 ZK-压缩,使用快速要点。

  • Solana 状态存储在完整节点的磁盘上,位于 “AccountsDB”
  • 存储的单位称为“账户”
  • 账户具有地址(每个 32 字节)
  • 账户可以存储的数据量各不相同——从 0 到最多 10 MB
  • 在 Solana 中存储 10 MB 大约需要 70 SOL,费用与存储有关,而非帐户的数量——可以是 1 个 10MB 或 1000 个 10KB 账户。
  • 当前所有 Solana 账户的总大小为 76GB(压缩)
  • 每笔 Solana 交易需要指定所有读取和写入的账户
  • Solana 交易目前限制在 1232 字节(已提出增加此限制的提案)
  • 每笔 Solana 交易需要指定一些文本
    • 签名(每个 64 字节)
    • 账户(每个 32 字节)
    • 指令数据(任意长度)
    • 最近区块哈希(32 字节)
    • 程序地址(每个 32 字节)(CPI:跨程序调用)
  • Solana 交易包括一个 32 字节的“最近区块哈希”,该哈希必须在最近的 150 个区块内,否则会被视为无效,需要重新签名和重新提交

正常交易的生命周期

当一笔交易典型地执行时,生命周期如下:

  1. 年限检查(只有最近的交易有效)、去重、结构验证、费用(气体)和签名检查首先在交易上执行
  2. 根据程序地址加载程序字节码,并实例化 Solana 虚拟机 (SVM)
  3. 检查所有交易引用的账户,从存储加载到内存,并传递给 SVM
  4. 执行程序字节码
  5. 任何被修改的账户都同步回其更新的存储状态

ZK-压缩的主要动机是:

  • 链上状态是昂贵的。例如,一千个账户需要 70 SOL,因此像 Drip Haus 这样的产品迅速变得昂贵
  • 即使没有完整的状态 Merkel 化,存储在磁盘上的更多账户也意味着更大的快照、较大的索引等等
  • 并非所有账户都经常访问,因此持续产生资源成本是没有必要的

那么,实现压缩的最简单方法是什么?

而不是将账户存储在磁盘上并在需要时读取它们(交易执行生命周期中的第 3 步),可以将账户数据作为有效载荷的一部分传递,节省与链上存储相关的费用。但这便引出了一个新问题——如何强制执行规则,确保用户没有撒谎账户的状态?

例如,假设一个存储代币余额的账户的链下值是 1200,并且其所有者字段为“BYixJwV32DjeuyRww72PwZMyKcaedN533GBrv7CDh4n9”。

如果你以该数据提交一个交易到链上,那链上如何知道你没有撒谎地址为“BYixJwV32DjeuyRww72PwZMyKcaedN533GBrv7CDh4n9”拥有哪些代币?

毕竟,处理该交易的完整节点无法访问链下数据——它期望你随交易提供该数据。

可以使用 Merkle 证明来解决这个问题。在不详细解释 Merkle 证明的情况下,可以认为这是以一种可验证的方式对一些数据进行“承诺”,并具有小的链上存储足迹。所有与链同步的完整节点都存储该小的“承诺”,当有人在交易中提供数据时,他们还可以在同一交易中提供一个可以验证承诺的“证明”。这个证明是密码学上安全的。

这有什么问题吗?

问题在于,Merkle 证明可能很大。如果一棵树包含 100,000 个账户,那么其中一个账户的证明大小为 17 * 32 = 544 字节。如果你想提供多个账户的证明,最坏情况下,最终会乘以证明大小,因此十个账户在最坏情况下将为 10 * 544 = 5440 字节。这个空间问题是特定于 Solana 的,因为 Solana 交易当前限制为 1232 字节,而其他链往往限制较少。请参阅上面的部分,我们列出了每个组件的大小——程序、签名、最近区块哈希等。因此,即便在最佳情况下,你仅用于 Merkle 证明就使用了一半的交易大小。

这里会出现几个问题。

如果交易大小是 1232 字节,你怎么能作为交易的一部分发送完整数据?

这是个很好的问题。当账户数量非常庞大且数据量较小时,ZK 压缩非常有用。代币余额(每个代币 8 字节)、NFT 的小量元数据等——100 字节的数据可以轻松放入一笔交易。1000 字节很难,因为还有其他内容需要放入。如果你的账户需要存储更大数量的数据,则此方法(和 ZK 压缩)将无法工作。

此声明的一个细微差别是,ZK 压缩中使用的相同逻辑可以用于账户状态的部分。这意味着虽然在单个交易有效载荷中放入账户的完整数据是不可能的,但仍有解决方法——具体来说,对于部分数据进行承诺和提供证明。

Merkle 证明是唯一的解决方法吗?

不。Merkle 证明只是一种向量承诺。它的承诺大小为 32 字节,证明大小为 Log2(N) * 32 字节,其中 N 是你要承诺的向量的大小。这就是我们从中得到了 17,因为 Log2(100000) 是 17。但也存在常量证明大小承诺( KZG, Pedersen);实际上,ZK-压缩使用的正是这种方法!

你说正常存储在完整节点的账户数据必须与交易一起提供。这些数据在哪里存储?

非常好的问题,这影响我们的信任假设和属性。答案是——任何地方!专门的 RPC 服务器可以存储这些数据;它可以是 FilecoinIPFS 的一部分,或者用户甚至可以将其存储在自己的机器上。重要的是,只要向量的所有元素都存储在某个地方,就可以动态计算证明。我们将在属性部分讨论这些数据存储位置的影响。

其他链能这样做吗?

ZK-压缩特别要求 zk-SNARK 验证成本低。因此,这在 Solana 上效果很好,因为计算的成本低于存储。使用向量承诺和证明提供给每个交易的数据的普遍概念也在其他链上可以实现,但计算昂贵意味着这个权衡在 Solana 上不那么突出。实际上,与一次性的 SSTORE 比较,持续的验证Gas费用在基于EVM的链上实际上更加昂贵。

ZK 压缩是什么?

  • 如果你不知道 ZK 压缩是什么,这一部分会涵盖它
  • 如果你对它有一个模糊的概念,我仍然建议你阅读这一部分,因为有一些常见的误解
  • 如果你完全知道它是什么,我会恳请你阅读它,以便你能在我说错时纠正我 :)

如果你理解上述内容,你就理解了 ZK 压缩的 90%。主要问题是 Merkle 证明的大小,因此,ZK 压缩就是使用一种方案来证明一次计算的结果。

如果你不知道 ZK 是什么,那也没关系。你只需要知道它是一种方法,用于证明你正确地执行了某项计算。一个简单的例子是你想证明你通过相乘得到第三个数字。也就是说,4*3 = 12

用“ZK 方法”证明这一点的函数是:

f(x,y) = x*y

如果你为上述代码生成一个电路,证明者生成一个正确计算的证明。电路本身就是承诺,因此所有人都知道你正在运行的“计算”是什么。但妙的是,他们并不需要知道输入值。一旦你运行 f(3,4),就返回 12 和“证明”。现在,任何人都可以拿着 12, "证明,",并验证你是否通过某种方式将两个数字相乘得到 12。他们不知道你是否执行的是 4,3,或者 6,2,甚至 12,1。你隐藏这些数字的事实,而其他人仍然可以进行验证,这便是“零知识”部分的来源。

我为什么解释这个?

这个概念是非常强大的,它可以证明你进行了一次特定的计算并得到了特定的结果。一旦某人拥有结果和证明,他们可以在不实际运行计算的情况下验证你是否正确完成了。这对任何任意计算都适用。我刚刚只是将两个数字相乘,但你甚至可以用它来表示“我验证了这十个签名,它们都是有效的”。这是第二个好处,也是零知识证明常被使用的最大原因之一,即使你不需要“隐藏”某些东西。因为你将一个需要运行 1000 步计算(甚至更多步骤)的问题,转化为只需要验证一个证明就能知道计算是否正确完成的问题。唯一的缺憾是,证明生成需要一些时间。

ZK 压缩使用相同的技术来运行实际的 Merkle 树成员资格逻辑。因此,它有一个电路可以获取账户数据、一个证明(128 字节),并验证数据确实是链上“承诺”的一部分(实际证明为 256 字节,但使用椭圆曲线和点的便利之处在于,如果你知道曲线,你只需一个点就能获得第二个点)。

这样做的主要目的是将证明大小减少到一个固定的 128 字节,这仍然为小账户数据留出了相对充足的空间。虽然正常的 Merkle 证明为 Log2(N),但 ZK 压缩始终是常量,因此可以在一个承诺下拥有非常大的账户数量。(供参考,100,000 个账户的 Merkle 证明大约为 550 字节,这是交易有效载荷的一半)

生成这个证明可以在链下进行,但验证这个证明必须在链上进行,因为程序需要确认你提供了正确的账户数据,然后才能允许执行继续进行。验证 ZK 证明的基本机制必须存在才能做到这一点。ZK-压缩使用的特定证明系统称为 Groth16,它又依赖于 alt_bn128 系统调用,目前在主网处于功能封闭状态,并正在测试中。

有趣的是,ZK-压缩所使用的机制可以用来验证任意计算(不仅仅是“这个叶子是否属于具有这个根的树?”)。

ZK 压缩的一个主要好处是,它为开发人员提供了所有处理“ZK”部分所需的工具,开发人员从 They 将它视为仅仅是具有相同字段的其他帐户,因此在程序内部,它可以作为常规帐户处理。将大部分“ZK 魔法”抽象化,而不让开发人员处理它是一种价值。

ZK Rollups

不给予过多细节,ZK rollups 大多使用 ZK-压缩使用的相同概念。主要的相似之处在于整个 rollup 状态表示为基础层(以太坊)中的单一根,这就是为什么会有一些说法认为 ZK-压缩是一个 rollup。然而,仍然有至关重要的差异。

让我们考虑 100 笔 rollup 交易。

整个 ZK rollup 被视为一个电路(就像我们用作示例的乘法程序)。所有 100 笔交易都经过验证(签名、合约逻辑、去重检查等),并为“ 在应用 100 笔交易后,状态根将从 A 变为 B”生成一个单一证明。一旦验证了证明,智能合约将状态根从 A 更新到 B。

然而,在 ZK-压缩中,这 100 笔交易中的每一笔都包含一个证明,简单地告诉你 账户数据是正确的,但状态转移(由交易生成)实际上是在链上作为 SVM 自身的一部分执行的。一旦验证了证明,它就会被视为常规账户。这在我们接下来讨论的组合性属性方面至关重要。

属性重审

现在我们来到了有趣的部分。ZK-压缩保留了 Solana 的哪些属性

同步原子组合性

如果我有一笔交易引用了 2 个 ZK 压缩账户和 10 个“正常”账户,它不会破坏组合性特性。引用 ZK 压缩账户的指令可以调用引用“正常”未压缩账户的另一指令/程序。即使两个账户在不同的树下被压缩,这个特性也完全保留。如果一个指令失败,整个交易将回滚(原子性),在第 1 行调用的指令的变化对第 2 行是可见的(同步性)。

这一点对于 rollup 并不适用,因为 ZK rollups 既不能同步地也不能原子地相互调用(除非它们采取全局锁并允许跨 rollup 的回滚)。

并行性

这个特性对并行性有一些影响,每种情况都值得考虑:

向同一树下的多个压缩账户写入

每个树都是在它自己的基础上进行并发的。这意味着如果用户正在读取/写入同一状态根下的两个压缩账户,它们可以并发执行,并且状态根可以并发更新。这里的逻辑与 Solana 在 cNFT 中使用的用于并发 Merkle 树更新的逻辑相同。

向同一压缩账户写入

每个压缩账户不是并发的。如果两个用户尝试写入同一个压缩账户,无论顺序如何,其中一个交易都会失败。在正常执行期间,来自上一指令对账户的写入在下一指令中是可用的,但对于 ZK 压缩账户,账户数据的证明将无效,因为它是前一个状态的证明。

还有一点要注意的是,压缩的大计算单位 (CU) 使用降低了每棵树的最大并发性,因为每个账户每个区块只能使用 12M 计算单元,考虑到 账户 CU 限制

注意

虽然同步原子组合性是大多数 rollup 的一个问题,但并行性属性更多地是在强调 ZK 压缩没有额外组件所需,比如排序器等。缺乏排序器的意味着基础链正在执行排序。现在我们可以讨论这一属性如何在Based Rollup 的情况下也为真,但对于大多数 rollup, 由于它们使用中心化排序器进行排序,因此这一点并不真实。

信任假设

虽然任何人都可以存储生成证明和提交交易所需的所有原始数据,但这是一个额外的信任假设,影响压缩状态的活性。如果由于某种原因,数据“丢失”或者出现延迟,那么,如果你没有存储数据,便无法提交交易。幸运的是,这种情况被称为 f+1 问题,而不是 3f+1 问题,它需要 拜占庭容错。一个 f+1 问题只需要一个诚实的节点来提供数据,并且由于证明是“自我可验证的”,因此没有“安全性”问题。主要存在“活性”问题和一个审查向量。

常规 rollups 和 ZK-压缩都需要提供有效性证明。但是,虽然 rollups 将完整状态转移功能编码在有效性证明中,ZK-压缩仅编码“账户数据是否正确?”因此,信任假设在这里略有不同。在压缩的情况下,信任假设主要涉及状态访问(而状态转移则是完全的)。而在 rollup 的情况下,信任假设则涉及完整状态转移功能(从基础层的视角)。关于比特数或基础问题的难度/不可解性的安全假设是相同的(双线性 Diffie-Hellman 假设),但你所信任的安全模型对象是不同的——状态访问与执行。我提到这一点是因为了解额外信任假设的来源很重要。

目前,验证 ZK 压缩账户的程序是可升级的,但由于它只执行一个高度特定的操作(开启 Merkle 证明),将来可以使其不可变或被冻结,而不会真正需要常规升级。

此外,状态压缩还可以通过两种方式实现。

  1. 提高交易大小的提案(proj-3x-tx 频道在 Solana discord 中)正在开发中。 一旦推出,如果大小可行,你可以使用常规模型的 Merkle 证明。
  2. 一旦 alt_bn128 系统调用实现,它也可以用于常量证明大小的常规向量承诺(KZG 可与任何配对友好的曲线协同工作,包括 alt_bn128)。这不需要 ZK 证明者电路

我们称之为什么?

不幸的是,像 rollup、L2 和 Validium 这样的术语被非常松散地应用,以至于一些 rollup 根本不是 rollup。它们不继承基础层的活性、安全性或抵制审查性。虽然 Helius 被指控使用“营销术语”,这些人出于相同的原因对投入的项目使用“rollup”这一术语——营销。事实上,非 rollup 项目以不同的阶段标记,仅仅是为了继续称其为 rollup。

并非所有人都对此负责——一些人对使用精确术语非常诚实,并花费了人月的时间与那些使用不精确术语来误导用户的人辩论(感谢 Toghrul,他不断呼吁每个人使用精确的术语)。

鉴于它具有一些与 rollup 不同的属性和信任假设,称其为 rollup 可能会让用户困惑。称其为 Validium 也过于宽泛,因为它忽略了同步原子组合性或并行性未被打破,数据可用性 (DA) 是链上的,更不用说,状态转移功能本身是无信任的(因为完整节点正在完全执行实际程序,而不仅仅是验证执行本身的有效性证明)。一些人可能会争辩说 ZK 证明是无信任的,但这显然不是事实——尽管它们是大大降低了信任的——在数学上,这些安全假设并不相同(它们可能对 99% 的用例是足够的,但相对于完整节点,它确实施加了一个额外的信任假设——例如,在使用配对曲线的 zk-SNARK 情况下的 双线性 Diffie Hellman 假设)。但当然,由于 ZK-压缩使用 snark 检查账户的有效性,因此说 ZK-压缩不是无信任的也是公平的——ZK-压缩账户的信任假设并不存在于“正常”账户中。因此,它在无信任和全面的 ZK rollup 之间。

如果我们用名称来推断属性,那么我会主张称其为“rollup”并不传达这些属性或信任假设的存在。或许想出一个新名称?ZK-压缩听起来不错,只要信任假设明确。

  • 原文链接: helius.dev/blog/solana-b...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Helius
Helius
https://www.helius.dev/