详解 Aztec 技术证明压缩,它如何使 DeFi 隐私交易更加便宜易用?

原文标题:Proof Compression

原文作者:Luke Edwards

原文来源:medium

编译:Captain Hiro, DeFi之道

Aztec 一直在努力使通用的零知识交易尽可能容易的进行,而零知识密码学的快速发展有助于这一过程的实现。在这篇文章中,我们想描述一种帮助我们达到这一目的的基本技术。

证明系统设计中需要权衡考虑的因素

在一个零知识证明系统中,当两方相互接触时,一方需要能够说服另一方相信某些陈述是真的。这其中的一方,我们称之为证明者,而证明者希望说服的另一方,就叫做验证者,证明者和验证者之间拥有某种特定的信息,同时他们要尽可能少的去分享这些信息。这一过程通常可以简单的分解为两个阶段:证明生成和证明验证。在理想的情况下,这两个阶段应该都是高速和高计算效率的,但现实是人们往往不得不对这两个阶段进行权衡。(在绝对的计算意义上,证明构建通常比验证过程要更加昂贵,因此当我们谈论例如“快速”的进行构建时,我们通常指的是相对于一些有意义的基准线更加快速,而不是相对于验证过程更加快速)

在 Aztec 的零知识 rollup 的设置中,实际上有三个不同的方面参与到了 SNARK 证明的创建和验证过程中,并且每个方面都有不同的计算能力和限制条件:

  • 用户:用户拥有隐私信息,如他们的余额和他们的交易历史,他们希望在参与交易和与 DeFi 协议的交互的过程中对这些信息进行保密。他们会在网络浏览器中生成证明,这个过程通常是在手机等低功率设备上。
  • rollup 提供者:rollup 提供者将用户交易进行分批压缩,这个过程涉及证明生成以及证明验证,至少在某种意义上是这样的(见下文关于递归的更多内容)。他们一般可以使用商业级别的硬件设施。
  • 区块链:以太坊虚拟机(EVM)是一个高度受限的计算环境,基于此的每个操作都有不同的成本,但通常很昂贵,并且必须由用户来支付交易费用。以太坊虚拟机负责验证一个证明(它封装了许多其他证明)。

为了给 Aztec 的用户带来能负担得起的、真正的零知识交易,我们将不同的证明系统(特别是 PlonK 的不同、美味的风味)跨越递归(recursion)的边界融合在一起。对于用户来说,这意味着他们在客户端证明生成过程中减少了计算成本,以及通过将以太坊虚拟机的计算最小化来降低货币成本。

递归

递归基于的是以下强有力的观察:零知识证明的验证本身就是一个可以由零知识证明来证明的计算。换句话说,没有什么可以阻止我们用一个证明 p* 来代替验证一个 SNARK 证明 p 的行为,即 p 已经被正确验证了。当然,通过这样做,我们引入了 p*,它本身必须被验证。但这从一开始就暗示了一个概念,即两个不同的证明系统是如何被结合或“编写(compose)”的。

让我们把一个证明系统看作是一对算法(P,V),它代表一个证明者(P)和一个验证者(V)。假设我们有一个证明系统(P,V),它有廉价的证明构造,但其证明验证过程却非常昂贵,还有一个证明系统(P*, V*),它有构造非常昂贵,但是验证过程是廉价的。那有没有一种方法可以将它们组合起来并获得两者的好处呢?请大家考虑以下过程:

在证明系统 P 下构建一个证明 ?。

在证明系统 P* 下构建证明 ?*,其中 p* 是证明 ? 在 V 下被正确验证的证明。

验证 ?*。

最终,这样验证的好处来自于这三个步骤中的每一步都可以由具有不同计算能力的各方来执行。通过这个想法延伸出的一个版本,正是使 Aztec Connect 能够向其用户提供廉价隐私交易的原因。而这里所说的两个证明系统就是 PlonK 和 TurboPlonK,前者有廉价的验证,后者有高效的证明构造。我们在 rollup 中使用这种组合技术的简化过程如下:

  1. 客户端使用 TurboPlonK 来构建一个证明 ?-Client,这是一个带有自定义门的 PlonK 变式(下面会有更多介绍),它允许在受限的设备上更高效的生成证明。它们将 ?-Client 发送到 rollup 提供者那里。
  2. rollup 提供者随后接收 ?-Client,并构建一个标准(即非 Turbo)PlonK 证明 ?-Rollup,其中证明 ?-Client 已经被正确验证。从这个意义上来讲,rollup 提供者一直受制于 TurboPlonK 验证和标准 PlonK 证明构造的高成本。但是,rollup 提供者是强大的,它可以同时处理两种证明系统,同时还可以赚钱并为用户节省大量的资金。证明 ?-Rollup 最终被发送到以太坊上。
  3. 以太坊虚拟机通过部署到以太坊的智能合约 StandardVerifier.sol ‌来验证?-Rollup。

注意:为了进一步优化压缩过程,一个 Aztec 的 rollup 提供者实际上参与了三个递归步骤,两个 Turbo-to-Turbo(使用 rollup‌ 和根 rollup‌ 线路,以这里‌解释的方式进行组装)和一个 Turbo-to-Standard(根验证器‌线路)。

自定义门(Custom Gates)

我们想通过一个假想的案例来说明 Turbo 和 Standard PlonK 之间的权衡问题。我们的目标是尽可能的将密码学概念黑箱化,但我们要提醒读者的是,下面的内容可能多少会有一点技术含量。

情景:你在一家名为 JazzTec 的公司担任协议设计师。你设计并实现了一个名为 PlunK 的零知识证明系统,该系统允许用户证明包括乘法和加法组合的语句,如 a+b=c,(a+b)-c=d,a-b-c=d,等等。你的小型证明系统只包含一种门,它通过符号可以表述为:

零知识证明

这里的 w 代表 “wire”,l = 左,r = 右,o = 输出,q 是一个“选择器(collector)”,用于在乘法和加法之间进行切换。JazzTec 的线路编写员做了一个应用程序,用户(或验证者)必须证明他们已经找到了数字 a、b、c 和 d,以便:

零知识证明

线路编写者为用户提供了一个模板:

零知识证明

从原理上讲,该线路看起来像是这样的:

零知识证明

通过在模板中填入 a、b、z、c 和 d 这些数值:

零知识证明

用户可以证明满足要求数值的知识。(嗯,整个过程几乎就是这样的。我们忽略了一个事实,即 z 的两个实例之间是必须是有连接的。这与复制限制条件的概念有关,这一点通常是非常重要的,但对于我们现在的观点来可有可无)。

在使用 PlunK 一段时间后,JazzTec 决定增加一个新的功能,该功能要求用户证明 x⁵=y 这样的语句。(例如,最近开发的专门为零知识证明系统设计的 Poseidon Hash‌,它需要进行五次幂的计算)。那么我们该如何使用 PlunK 实现这种类型的操作呢?我们的表达式可以分解为一系列的乘法和一个加法,即:

零知识证明

因此,为了处理这种操作而设计的线路部分可能看起来像是这样的:

零知识证明

将这个新的组件添加到线路模板中后,其执行轨迹可以是这样的:

零知识证明

现在我们来考虑另一种方法。如果我们不使用 PlunK 的更简单的设置,而是将证明系统扩展为一个对 x⁵=y 形式的语句提供本地支持的系统,那么情况会如何呢?这里的关键思想是,线路编写者向验证者指示要证明的语句的形式,并且可以添加一个新的选择器,为执行轨迹中的每一行引入额外的解释。

我们将把这种新的、扩展的证明系统称为 TangoPlunK。它包含一个新的自定义选择器,其通用门(generic gate)的形式为:

零知识证明

我们看到,新的 q-custom 选择器可以开启或关闭自定义功能;当 q-custom 为 1 时,我们就有了五次幂方程,而当 q-custom 为 0 时,我们就有了旧的标准 PlunK 方程。现在,x⁵=y 所涉及的操作可以被封装到一个单一的门中,它看起来像是这样:

零知识证明

通过使用 TangoPlunK,我们程序的执行轨迹就会变成了这样:

零知识证明

总而言之,以前完成整个验证过程需要 6 个门,而现在就只需要 3 个,所以我们通过改用 TangoPlunK,将原有线路的复杂度减少了 50%!

对 TurboNerds 的一些补充

有经验的读者可能已经注意到了,为了处理五次幂类型的操作而引入的自定义门也增加了要证明的恒等式的程度。一般来说,这意味着验证者的计算量将会增加,从而可能会抵消一些从减少线路大小中获得的好处。在我们上面的例子中,这相当于将恒等式的数量增加了一倍(从 PlunK 的 3 到 TangoPlunK 的 6),但是整个线路的大小却减少了一半(巧合的是,它从 6 减少到了 3)。如果在我们的程序中可由定制门表示的计算比例较小,那么我们就必须重新考虑其效用。在实践中,这种权衡必须被考虑在内。

同样重要的是,我们可以设想出一些有用的自定义门,它们不会增加恒等式的数量,或者至少不会让验证者感受到这种影响。例如,在最初的 TurboPlonK ‌证明系统中就是如此,其中关键的创新是引入了自定义门,它在计算 Pedersen Hash 的情况下促进了椭圆曲线标量乘法的有效进行。

证明者和验证者的成本

我们以 PlunK 和 TangoPlunK 为例,旨在强调在设计证明系统时如何对于计算过程进行权衡,其依据是:(1)验证器的复杂度从根本上与线路中的门数有关;(2)验证器的复杂度与线路的大小相对独立,但它会随着被证明的要求或恒等式的复杂度而增加。

对于我们的样本程序来说,TangoPlunK 的证明构造相对便宜,因为引入我们的定制门可以大大减少线路的大小。而在 TangoPlunk 中,验证则是相对昂贵的,因为验证者不仅看不到线路大小减少的好处,他们还必须面对一个相对于 PlunK 来说更加复杂的恒等式验证。

更具体地说,在像 PlonK 这样的证明系统中,验证者和核查者的很大一部分计算成本是由于需要计算昂贵的椭圆曲线标量乘法而产生的。对于验证者来说,这些操作来自于在构建证明?时需要计算的多项式承诺(commitment)。需要进行的标量乘法(scalar multiplication)的数量与被承诺的多项式的复杂性成正比,而这又与线路中门的数量成正比。(对于那些不熟悉多项式在 SNARKs 中所扮演的重要作用的人来说,我们建议你们阅读 Vitalik Buterin 的这篇文章‌)

另一方面,对于验证者来说,在使用 ? 中从证明方收到的承诺重构原始声明或恒等式时,主要需要的是标量乘法。特别是在验证过程中,恒等式中的额外选择器会直接导致额外的标量乘法。例如,大家可以看一下标准 PlonK 验证算法的第 9 步:

零知识证明

这个表达式中的 q 是标准 PlonK 的选择器,而运算符“·”代表椭圆曲线标量乘法。

上面给大家提供的例子说明了这种权衡:与标准 PlunK 相比,TangoPlunK 允许门的数量减少 50%,从而进一步降低了验证器的成本(至少在承诺方面是这样),同时也增加了验证器的复杂性(通过增加选择器 q-custom)。

当然,我们在这个例子中暗指的现实用例是标准 PlonK 证明系统和 TurboPlonK 之间的类似权衡。这就是为什么在我们 Aztec 的应用中,比如 Aztec Connect,我们安排证明者使用 TurboPlonK,而验证者使用的是 Standard PlonK。

进一步的探索

在过去几年的事件里,零知识密码学已经出现了爆炸性的发展。在 Aztec,我们正在从 TurboPlonK 升级到我们所说的 UltraPlonK,它相当于是 TurboPlonK + Plookup‌,这是我们设计的一个协议,用于使用查找表来进一步加快证明的构建,但验证者则需要付出额外的成本。在另一个极端情况下,我们设计了一个叫做 Fflonk‌ 的协议,它允许在验证者支付额外的成本的前提下进行极其有效的验证。Fflonk 利用了这篇文章‌的承诺方案(我们称之为 SHPLONK),它有可能将我们的以太坊虚拟机执行成本降低 35%。

结论

零知识证明是一个非常酷的、具有巨大技术潜力的前沿概念。我们在这里所描述的技术是利用这种潜力来创造一个更好的互联网的一小步。谢谢大家能加入我们的行列,并与我们一同前行。

感谢 Jon Wu、Ariel Gabizon、Suyash Bagad、Innokentii Sennovskii 和 Guillaume Drevon 对本文内容的积极评论和讨论。

责任编辑:Kate

转载声明:本文 由CoinON抓取收录,观点仅代表作者本人,不代表CoinON资讯立场,CoinON不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。若以此作为投资依据,请自行承担全部责任。

声明:图文来源于网络,如有侵权请联系删除

风险提示:投资有风险,入市需谨慎。本资讯不作为投资理财建议。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 2022年9月17日 上午3:52
下一篇 2022年9月17日 上午3:52

相关推荐

详解 Aztec 技术证明压缩,它如何使 DeFi 隐私交易更加便宜易用?

星期六 2022-09-17 3:52:05

Aztec 一直在努力使通用的零知识交易尽可能容易的进行,而零知识密码学的快速发展有助于这一过程的实现。在这篇文章中,我们想描述一种帮助我们达到这一目的的基本技术。

证明系统设计中需要权衡考虑的因素

在一个零知识证明系统中,当两方相互接触时,一方需要能够说服另一方相信某些陈述是真的。这其中的一方,我们称之为证明者,而证明者希望说服的另一方,就叫做验证者,证明者和验证者之间拥有某种特定的信息,同时他们要尽可能少的去分享这些信息。这一过程通常可以简单的分解为两个阶段:证明生成和证明验证。在理想的情况下,这两个阶段应该都是高速和高计算效率的,但现实是人们往往不得不对这两个阶段进行权衡。(在绝对的计算意义上,证明构建通常比验证过程要更加昂贵,因此当我们谈论例如“快速”的进行构建时,我们通常指的是相对于一些有意义的基准线更加快速,而不是相对于验证过程更加快速)

在 Aztec 的零知识 rollup 的设置中,实际上有三个不同的方面参与到了 SNARK 证明的创建和验证过程中,并且每个方面都有不同的计算能力和限制条件:

  • 用户:用户拥有隐私信息,如他们的余额和他们的交易历史,他们希望在参与交易和与 DeFi 协议的交互的过程中对这些信息进行保密。他们会在网络浏览器中生成证明,这个过程通常是在手机等低功率设备上。
  • rollup 提供者:rollup 提供者将用户交易进行分批压缩,这个过程涉及证明生成以及证明验证,至少在某种意义上是这样的(见下文关于递归的更多内容)。他们一般可以使用商业级别的硬件设施。
  • 区块链:以太坊虚拟机(EVM)是一个高度受限的计算环境,基于此的每个操作都有不同的成本,但通常很昂贵,并且必须由用户来支付交易费用。以太坊虚拟机负责验证一个证明(它封装了许多其他证明)。

为了给 Aztec 的用户带来能负担得起的、真正的零知识交易,我们将不同的证明系统(特别是 PlonK 的不同、美味的风味)跨越递归(recursion)的边界融合在一起。对于用户来说,这意味着他们在客户端证明生成过程中减少了计算成本,以及通过将以太坊虚拟机的计算最小化来降低货币成本。

递归

递归基于的是以下强有力的观察:零知识证明的验证本身就是一个可以由零知识证明来证明的计算。换句话说,没有什么可以阻止我们用一个证明 p* 来代替验证一个 SNARK 证明 p 的行为,即 p 已经被正确验证了。当然,通过这样做,我们引入了 p*,它本身必须被验证。但这从一开始就暗示了一个概念,即两个不同的证明系统是如何被结合或“编写(compose)”的。

让我们把一个证明系统看作是一对算法(P,V),它代表一个证明者(P)和一个验证者(V)。假设我们有一个证明系统(P,V),它有廉价的证明构造,但其证明验证过程却非常昂贵,还有一个证明系统(P*, V*),它有构造非常昂贵,但是验证过程是廉价的。那有没有一种方法可以将它们组合起来并获得两者的好处呢?请大家考虑以下过程:

在证明系统 P 下构建一个证明 ?。

在证明系统 P* 下构建证明 ?*,其中 p* 是证明 ? 在 V 下被正确验证的证明。

验证 ?*。

最终,这样验证的好处来自于这三个步骤中的每一步都可以由具有不同计算能力的各方来执行。通过这个想法延伸出的一个版本,正是使 Aztec Connect 能够向其用户提供廉价隐私交易的原因。而这里所说的两个证明系统就是 PlonK 和 TurboPlonK,前者有廉价的验证,后者有高效的证明构造。我们在 rollup 中使用这种组合技术的简化过程如下:

  1. 客户端使用 TurboPlonK 来构建一个证明 ?-Client,这是一个带有自定义门的 PlonK 变式(下面会有更多介绍),它允许在受限的设备上更高效的生成证明。它们将 ?-Client 发送到 rollup 提供者那里。
  2. rollup 提供者随后接收 ?-Client,并构建一个标准(即非 Turbo)PlonK 证明 ?-Rollup,其中证明 ?-Client 已经被正确验证。从这个意义上来讲,rollup 提供者一直受制于 TurboPlonK 验证和标准 PlonK 证明构造的高成本。但是,rollup 提供者是强大的,它可以同时处理两种证明系统,同时还可以赚钱并为用户节省大量的资金。证明 ?-Rollup 最终被发送到以太坊上。
  3. 以太坊虚拟机通过部署到以太坊的智能合约 StandardVerifier.sol ‌来验证?-Rollup。

注意:为了进一步优化压缩过程,一个 Aztec 的 rollup 提供者实际上参与了三个递归步骤,两个 Turbo-to-Turbo(使用 rollup‌ 和根 rollup‌ 线路,以这里‌解释的方式进行组装)和一个 Turbo-to-Standard(根验证器‌线路)。

自定义门(Custom Gates)

我们想通过一个假想的案例来说明 Turbo 和 Standard PlonK 之间的权衡问题。我们的目标是尽可能的将密码学概念黑箱化,但我们要提醒读者的是,下面的内容可能多少会有一点技术含量。

情景:你在一家名为 JazzTec 的公司担任协议设计师。你设计并实现了一个名为 PlunK 的零知识证明系统,该系统允许用户证明包括乘法和加法组合的语句,如 a+b=c,(a+b)-c=d,a-b-c=d,等等。你的小型证明系统只包含一种门,它通过符号可以表述为:

零知识证明

这里的 w 代表 “wire”,l = 左,r = 右,o = 输出,q 是一个“选择器(collector)”,用于在乘法和加法之间进行切换。JazzTec 的线路编写员做了一个应用程序,用户(或验证者)必须证明他们已经找到了数字 a、b、c 和 d,以便:

零知识证明

线路编写者为用户提供了一个模板:

零知识证明

从原理上讲,该线路看起来像是这样的:

零知识证明

通过在模板中填入 a、b、z、c 和 d 这些数值:

零知识证明

用户可以证明满足要求数值的知识。(嗯,整个过程几乎就是这样的。我们忽略了一个事实,即 z 的两个实例之间是必须是有连接的。这与复制限制条件的概念有关,这一点通常是非常重要的,但对于我们现在的观点来可有可无)。

在使用 PlunK 一段时间后,JazzTec 决定增加一个新的功能,该功能要求用户证明 x⁵=y 这样的语句。(例如,最近开发的专门为零知识证明系统设计的 Poseidon Hash‌,它需要进行五次幂的计算)。那么我们该如何使用 PlunK 实现这种类型的操作呢?我们的表达式可以分解为一系列的乘法和一个加法,即:

零知识证明

因此,为了处理这种操作而设计的线路部分可能看起来像是这样的:

零知识证明

将这个新的组件添加到线路模板中后,其执行轨迹可以是这样的:

零知识证明

现在我们来考虑另一种方法。如果我们不使用 PlunK 的更简单的设置,而是将证明系统扩展为一个对 x⁵=y 形式的语句提供本地支持的系统,那么情况会如何呢?这里的关键思想是,线路编写者向验证者指示要证明的语句的形式,并且可以添加一个新的选择器,为执行轨迹中的每一行引入额外的解释。

我们将把这种新的、扩展的证明系统称为 TangoPlunK。它包含一个新的自定义选择器,其通用门(generic gate)的形式为:

零知识证明

我们看到,新的 q-custom 选择器可以开启或关闭自定义功能;当 q-custom 为 1 时,我们就有了五次幂方程,而当 q-custom 为 0 时,我们就有了旧的标准 PlunK 方程。现在,x⁵=y 所涉及的操作可以被封装到一个单一的门中,它看起来像是这样:

零知识证明

通过使用 TangoPlunK,我们程序的执行轨迹就会变成了这样:

零知识证明

总而言之,以前完成整个验证过程需要 6 个门,而现在就只需要 3 个,所以我们通过改用 TangoPlunK,将原有线路的复杂度减少了 50%!

对 TurboNerds 的一些补充

有经验的读者可能已经注意到了,为了处理五次幂类型的操作而引入的自定义门也增加了要证明的恒等式的程度。一般来说,这意味着验证者的计算量将会增加,从而可能会抵消一些从减少线路大小中获得的好处。在我们上面的例子中,这相当于将恒等式的数量增加了一倍(从 PlunK 的 3 到 TangoPlunK 的 6),但是整个线路的大小却减少了一半(巧合的是,它从 6 减少到了 3)。如果在我们的程序中可由定制门表示的计算比例较小,那么我们就必须重新考虑其效用。在实践中,这种权衡必须被考虑在内。

同样重要的是,我们可以设想出一些有用的自定义门,它们不会增加恒等式的数量,或者至少不会让验证者感受到这种影响。例如,在最初的 TurboPlonK ‌证明系统中就是如此,其中关键的创新是引入了自定义门,它在计算 Pedersen Hash 的情况下促进了椭圆曲线标量乘法的有效进行。

证明者和验证者的成本

我们以 PlunK 和 TangoPlunK 为例,旨在强调在设计证明系统时如何对于计算过程进行权衡,其依据是:(1)验证器的复杂度从根本上与线路中的门数有关;(2)验证器的复杂度与线路的大小相对独立,但它会随着被证明的要求或恒等式的复杂度而增加。

对于我们的样本程序来说,TangoPlunK 的证明构造相对便宜,因为引入我们的定制门可以大大减少线路的大小。而在 TangoPlunk 中,验证则是相对昂贵的,因为验证者不仅看不到线路大小减少的好处,他们还必须面对一个相对于 PlunK 来说更加复杂的恒等式验证。

更具体地说,在像 PlonK 这样的证明系统中,验证者和核查者的很大一部分计算成本是由于需要计算昂贵的椭圆曲线标量乘法而产生的。对于验证者来说,这些操作来自于在构建证明?时需要计算的多项式承诺(commitment)。需要进行的标量乘法(scalar multiplication)的数量与被承诺的多项式的复杂性成正比,而这又与线路中门的数量成正比。(对于那些不熟悉多项式在 SNARKs 中所扮演的重要作用的人来说,我们建议你们阅读 Vitalik Buterin 的这篇文章‌)

另一方面,对于验证者来说,在使用 ? 中从证明方收到的承诺重构原始声明或恒等式时,主要需要的是标量乘法。特别是在验证过程中,恒等式中的额外选择器会直接导致额外的标量乘法。例如,大家可以看一下标准 PlonK 验证算法的第 9 步:

零知识证明

这个表达式中的 q 是标准 PlonK 的选择器,而运算符“·”代表椭圆曲线标量乘法。

上面给大家提供的例子说明了这种权衡:与标准 PlunK 相比,TangoPlunK 允许门的数量减少 50%,从而进一步降低了验证器的成本(至少在承诺方面是这样),同时也增加了验证器的复杂性(通过增加选择器 q-custom)。

当然,我们在这个例子中暗指的现实用例是标准 PlonK 证明系统和 TurboPlonK 之间的类似权衡。这就是为什么在我们 Aztec 的应用中,比如 Aztec Connect,我们安排证明者使用 TurboPlonK,而验证者使用的是 Standard PlonK。

进一步的探索

在过去几年的事件里,零知识密码学已经出现了爆炸性的发展。在 Aztec,我们正在从 TurboPlonK 升级到我们所说的 UltraPlonK,它相当于是 TurboPlonK + Plookup‌,这是我们设计的一个协议,用于使用查找表来进一步加快证明的构建,但验证者则需要付出额外的成本。在另一个极端情况下,我们设计了一个叫做 Fflonk‌ 的协议,它允许在验证者支付额外的成本的前提下进行极其有效的验证。Fflonk 利用了这篇文章‌的承诺方案(我们称之为 SHPLONK),它有可能将我们的以太坊虚拟机执行成本降低 35%。

结论

零知识证明是一个非常酷的、具有巨大技术潜力的前沿概念。我们在这里所描述的技术是利用这种潜力来创造一个更好的互联网的一小步。谢谢大家能加入我们的行列,并与我们一同前行。

感谢 Jon Wu、Ariel Gabizon、Suyash Bagad、Innokentii Sennovskii 和 Guillaume Drevon 对本文内容的积极评论和讨论。

责任编辑:Kate