本文介绍了LambdaClass团队开发的Aleo区块链的替代实现,包括使用Tendermint共识层和使用arkworks框架实现的零知识虚拟机,目标是Aleo指令。文章还介绍了共识层、虚拟机以及VM与共识集成层,并指出了目前正在进行的工作,例如支持更多数据类型和指令、生成状态转换正确性的证明等。
在过去的 12 周里,我们在 LambdaClass 开发了 Aleo 区块链的另一种实现。我们要感谢 Aleo 的 Alex Pruden 和 Howard Wu 在整个过程中的支持。
从高层次上讲,该项目包括一个使用 Tendermint 的共识层和一个使用 arkworks 框架实现的、以 Aleo 指令为目标的零知识虚拟机。
你可以查看代码:
这个区块链的关键特性在于,它被设计成一个完全私有的平台,用户可以在上面开发应用程序,然后可以离线构建和执行这些应用程序,生成一个执行证明,然后将其发送到区块链节点进行验证和存储。
如果你需要一个在分布式系统、机器学习、编译器和密码学等领域合作了十年的工程师和研究人员团队,我们就是你要找的人。想更多地了解它吗?通过向我们发送电子邮件来预定与我们会面。
共识层负责验证执行状态更改的传入交易,并在任意数量的节点上复制这些交易(以及执行这些交易的顺序)。
为了实现这一点,我们决定利用 Tendermint Core,这是一个用 Go 编写的共识机制的实现。除了 Tendermint Core 二进制文件之外,你还需要运行你的 应用程序区块链接口(简称 ABCI)的实现。这个 ABCI 需要实现特定的Hook,Tendermint Core 在需要时通过套接字来调用这些Hook。例如,当接收到一个交易时,它会调用 CheckTx,这应该在将交易输入到 mempool 并将其转发到其他节点之前验证该交易。这种灵活的方法允许用任何语言编写 ABCI,只要它适当地响应调用即可。我们决定用 Rust 编写我们的实现。
你可以在这里查看此实现的的代码。该存储库还包含一个 CLI 应用程序,可以轻松地编译、部署和执行程序,并将这些交易发送到区块链。它还具有与帐户相关的其他几个功能,例如检索用户的余额或查看帐户拥有的 记录。我们将在本文的集成部分解释记录背后的动机,但它们本质上是在区块链中封装状态和所有权功能的一种方式。

考虑到 VM 的实现和区块链的要求,我们必须在共识层上做出一些设计决策。以下是 Tendermint Core 如何实现的一般概述:
credits 程序的密钥,作为内置默认值。该程序定义了信用记录。它本质上是一个原生的 Aleo 程序,具有用于管理信用记录的功能。make 目标来初始化和启动多个验证器,这些验证器可以在本地或远程网络上运行。Tendermint 支持向网络添加新节点。通常,网络中的节点可以以两种不同的模式工作:
要添加一个非验证器,该节点需要具有相同的创世块并指向持久对等节点(充当网络中固定节点的 IP 地址)。要将一个节点转换为验证器,ABCI 需要实现更新 Tendermint 节点的投票权的功能。
为此,我们实现了一个 stake 命令,通过将信用交换为质押记录来“冻结”信用(并增加验证器的投票权),你可以在需要时“取消质押”(相应地减少投票权)。
当一个节点是一个验证器时,它会在它参与的每个区块提交中获得奖励。
从高层次上讲,我们的 VM 提供了一个 API 来获取一个看起来像这样的 Aleo 程序:
program main.aleo;
function add:
input r0 as u16.public;
input r1 as u16.private;
add r0 r1 into r2;
output r2 as u16.public;
并为其生成一对证明密钥和验证密钥(这通常被称为构建或合成程序),允许任何人执行该程序并提供证明或验证所述证明。共识层使用它来部署程序(即,上传其验证密钥以及代码)、执行它们并验证它们。
在内部,此 VM 使用 Arkworks 作为后端。程序被转换为 Rank One Constraint System (R1CS),然后传递给 Marlin 证明者以供执行。当我们开始使用 Arkworks 时,我们注意到 API 的某些方面及其通用性正在成为开发人员的负担,因此我们围绕它创建了一个名为 Simpleworks 的薄包装器,以及一些基本文档。
给定以下 Aleo 程序
program foo.aleo;
function main:
input r0 as u64.public;
input r1 as u64.public;
add r0 r1 into r2;
output r2 as u64.public;
执行函数 main 将如下所示:
use lambdavm::jaleo::UserInputValueType::U16;
fn main() {
use lambdavm::{build_program, execute_function};
// Parse the program
let program_string = std::fs::read_to_string("./programs/add/main.aleo").unwrap();
let (program, build) = build_program(&program_string).unwrap();
let function = String::from("main");
// Declare the inputs (it is the same for public or private)
let user_inputs = vec![U16(1), U16(1)];
// Execute the function
let (_execution_trace, proof) = execute_function(&program, &function, &user_inputs).unwrap();
let (_proving_key, verifying_key) = build.get(&function).unwrap();
assert!(lambdavm::verify_proof(verifying_key.clone(), &user_inputs, &proof).unwrap())
}
我们的 VM 必须执行的最重要的任务是将程序转换为算术电路,因为其余的工作,即生成证明和验证它,使用 Arkworks API 非常简单。
在继续之前,你应该至少基本了解算术电路以及 Arkworks 如何让你使用它们。你可以在这里阅读有关它的信息。
要生成电路,我们执行以下步骤:
Program,其中包含有关它的所有相关信息(所有输入和输出指令的列表,它们是公开的还是私有的,所有常规指令(如 add 及其操作数)的列表等)。我们目前依赖 SnarkVM 的解析器,但计划编写我们自己的解析器。ConstraintSystem,它将在最后保存我们所有的电路约束。Gadget。你可以将gadget 视为算术电路中本机类型(如 u8)的等效项。如果输入是公开的,则 gadget 是公开的;否则,它会成为一个 witness,即私有的。在我们的示例中,第一个指令 input r0 as u16.public 变为对 UInt16Gadget.new_input(...) 的调用,第二个指令变为 UInt16Gadget.new_witness(...)。ConstraintSystem 中生成其约束。在我们的示例中,当我们遇到 add r0 r1 into r2; 指令时,我们调用 UInt16Gadget.addmany(...)。这是一个 arkworks 提供的函数,它将采用一个 UInt16 列表,将它们相加,使用所有关联的约束隐式地改变 ConstraintSystem,然后返回总和的值。并非所有指令都实现了相应的 arkworks 函数,因此对于这些指令,我们不得不自己编写。因为一个程序可以有多个寄存器相互作用,为了做到以上,我们必须跟踪每个寄存器和它在执行过程中的值。为此,我们在整个执行过程中保留一个内部哈希表。
此外,我们还运行了一些基准测试,将我们的 VM 与 Aleo 的 SnarkVM 进行了比较,我们的结果表明我们比它快几倍;详细信息将在另一篇文章中发布。基准测试的代码在我们的 VM Repo中。
上面,我们讨论了 VM 如何允许运行任意 Aleo 程序,这些程序可以部署、本地执行,然后在 Aleo 区块链上进行验证。每个 Aleo 交易要么是程序的部署,要么是程序执行的证明(这在技术上是不准确的,因为每个交易可以有多个这样的程序,但为简单起见,我们将忽略这一点)。在执行的情况下,节点使用程序的验证密钥来验证正确的执行,然后再将交易提交到区块。
在我们获得了一个基本的 VM 版本后,我们意识到获得一个功能齐全的 Aleo 区块链需要比上述更多的工作。如果事务证明某些计算已正确完成,那么它们的用处将非常小。为了有用,它们还需要 修改状态。在 Aleo 中,状态是通过 记录 来管理的,本质上是一个类似于比特币的 UTXO 模型。通常,当用户发送交易时,他们会花费他们拥有的一些记录来创建新的记录来代替它们。
因为 Aleo 是完全私有的,所以交易不能只发布它想要花费的记录以及签名;它必须在零知识中 证明 记录的所有权和存在,然后 加密 记录,以便只有它的所有者才能在线解密。
这意味着,为了与共识层集成并获得一个功能齐全的区块链,我们需要更多。VM 可以证明程序执行的正确性,但与交易相关的零知识证明还需要包括以下内容:
序列号(如果你了解 ZCash,可以将其视为 nullifier)。我们还讨论了如何将记录加密存储在链上,以便只有拥有记录所有者查看密钥的人才能解密它们(在 Aleo 中,查看密钥 只是与允许记录解密的帐户关联的另一个密钥)。
但这里有一个问题。例如,当用户 A 想要向用户 B 发送资金时,他们必须创建一个由 B 拥有的记录并对其进行加密,以便只有 B 才能解密它。但 A 不一定拥有 B 的查看密钥,只有他们的地址。这意味着 Aleo 使用的加密方案不能是对称的,因为这要求用户 A 拥有 B 的查看密钥才能向他们发送资金,而不仅仅是他们的地址。
为了实现这一点,记录使用一种名为 ECIES(椭圆曲线综合加密方案)的方案进行加密。我们不会详细介绍它的工作原理,但它是 Diffie-Hellman 密钥交换与对称加密方案的组合。
我们在 VM 和共识层之间引入了一个中间层来解决上述所有问题。这个中间层处理与记录、它们的加密以及状态转换证明所需的 snark 相关的所有事情。
在原始的 SnarkVM 实现中,这个中间层实际上并不存在,因为它是 VM 本身的一部分,但我们发现将这两个关注点分开更有益。
这个项目仍在积极开发中,并且正在进行一些工作。其中包括:
group 数据类型(椭圆曲线元素)和诸如 BHP 承诺之类的内容。你可以在 README 上查看完整列表。我们计划在未来四周内完成这些任务。虽然许多事情可以改进,但该项目已经可以投入生产。
我们有很多关于改进 SnarkVM 和 Aleo 的想法和评论,但我们会将其留到另一系列文章中。
- 原文链接: blog.lambdaclass.com/ope...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!