译者指南Cairo是一种ZK友好的高级语言,具有完整的图灵,也是以太坊L2-Starknet的合约开发语言,正在进行修改和升级。本文是分析Cairo 1.0系列文章的第一篇。作者Mathieu从Cairo高级语言到Cairo汇编,分析了Sierra作为中间层的设计动机和实现原理。本文提到了很多Cairo 0的问题和Cairo 1的改进,代码细节丰富。建议Cairo开发者阅读全文,了解更多关于Cairo 1.0的内容。
TL;DRSierra在高级Cairo编程语言和更原始的编译目标(比如CA)之间扮演着中间人的重要角色,确保生成的CA可以在Starknet上安全运行。其设计以安全为中心,使用一组函数生成安全的CA代码,结合强大的编译器和线性类型系统防止运行时错误,并内置Gas系统防止无限循环。在下一部分中,我们将重点了解Sierra程序的结构,并提供阅读和理解Sierra程序的基本要求。
马修最近参加了Starkware Sessions的两个会议,即沙哈尔·帕皮尼的“使用类型系统加强安全性”和奥里·齐夫的“不要停留在停顿问题上”。如果你想了解更多关于Cairo stack的信息,我强烈建议你观看这些视频。以下文章是系列文章中的第一篇,我将进一步了解Sierra,以便更好地了解Cairo、其机制以及Starknet整体。
Sierra(安全中间表示)是高级语言Cairo和编译目标(如Cairo Assembly(CA))之间的中间层。该语言旨在确保安全性并防止运行时错误。它使用编译器来检测编译时可能失败的操作,以确保每个函数都返回,并且没有无限循环。Sierra使用简单但功能强大的类型系统来表达中间层代码,同时确保安全性。这使得高效地编译成CA成为可能。
动机在Cairo 0中,开发者会用Cairo编写Starknet合同,编译成CA,直接部署编译到Starknet。用户可以通过调用智能合同功能,签署交易并将其发送到分拣机,与Starknet合同进行交互。分拣员将运行该交易以获取用户的交易费用,SHARP将为包含该交易的批次生成ZK证书,分拣员将收取包含该交易的交易费用。
Cairo 0交易流程
但是,这个Cairo 0进程会导致一些问题:
在Cairo中只能证明有效的语句,所以不能证明失败的交易。诸如assert 0 = 1的无效语句无法被证明,因为它被转换为不可满足的多项式约束。事务执行可能会失败,导致事务不包括在块中。在这种情况下,分拣员会做无报酬的工作。因为失败的交易没有有效证明,所以无法纳入,也没有办法强制分拣员收费。分类器可能会受到DDoS攻击。攻击者使用无效交易使其无用功,分拣员不能为运行这些交易收取任何费用。审查(当分类器故意决定不包括某些交易)和无效交易无法区分,因为两种类型的交易都不会包括在块中。在以太坊上,所有失败的交易都被标记为已恢复,但它们仍然包含在块中,允许验证者在失败时收取交易费。为了防止恶意用户用无效交易轰炸网络,淹没分拣员,使合法交易无法处理,Starknet需要一个类似的系统,允许分拣员对失败的交易收取费用。为了解决上述问题,Starknet网络需要达到两个目标:完整性和有效性。完整性确保事务执行总是可以被证明,即使预计会失败。有效性确保有效的交易不会被拒绝,从而防止审查。
Sierra是按合同正确结算的,它让分拣员对所有交易收费。我们可以部署分支代码(比如if/else),而不是可能失败的代码(比如asserts)。Cairo 1的断言被翻译成分支Sierra代码,允许错误传播回返回布尔值的原始入口点,指示事务的成功或失败。如果入口点的返回值为真/假,Starknet操作系统可以确定事务是否有效,并决定是否应用状态更新。如果交易成功。
Cairo 1提供了一种类似于Rust的语法,并通过抽象Sierra的安全结构创建了一种可证明的、开发人员友好的编程语言。它被编译成Sierra,这是一个正确构造的Cairo代码的中间表示,不包含任何失败语义。这确保了如果没有Sierra代码,它将会失败,并且最终会编译成CA的安全子集。开发人员可以专注于编写高效的智能合约,而不用担心编写非故障代码,所有这些代码都改进了安全原语。
开发者将把他们的Cairo 1代码编译成Sierra,在Starknet上部署Sierra程序,而不是在Starknet上部署CA代码。交易申报时,定序器会负责将Sierra代码编译成CA,确保失败的代码无法部署在Starknet上。
为了设计一种不会失败的语言,我们必须首先确定Cairo 0中的不安全操作。包括:
非法的内存地址引用;尝试访问未分配内存单元的断言,因为它们可能会失败并且无法恢复。由于Cairo的一次性写入内存模型,对同一个内存地址的多次写入是无止境的,这使得无法确定程序是否会退出,也无法保证在Cairo 0中解引用不会失败。开发人员可以编写以下代码来尝试访问未分配内存单元的内容。
let(ptr:felt *)= alloc();
tempvar x =[ptr];
Sierra的类型系统通过实施严格的所有权规则和使用智能指针(如Box)来防止常见的指针相关错误,因此可以在编译时检测并防止无效的指针取消引用。Box & ltT & gtType用作指向有效且已初始化的指针实例的指针,并为实例化和取消引用提供了两个函数:box_new()和box_deref()。通过使用类型系统在编译时捕获解引用错误,从Sierra编译的CA可以避免无效指针的解引用。
确保没有存储单元将被重复写入。在Cairo 0中,用户将使用以下数组:
let(array:felt *)= alloc();
断言array[0]= 1;
断言array[1]= 2;
断言array[1]= 3;//失败
但是,尝试两次写入同一个数组索引会导致运行时错误,因为内存单元只能写入一次。为了避免这个问题,Sierra引入了一个数组
为了确保以前使用的已附加的数组实例不会被重用,Sierra使用线性类型系统来确保对象只被使用一次。因此,任何已附加的数组实例都不能在另一个array_append调用中重用。
下面的代码展示了一个Sierra程序的片段,它创建了一个felt数组,并使用array_append库函数将值1追加了两次。在代码中,对array_append的第一次调用将id为[0]的数组变量作为输入,并返回表示更新后的数组的id[2]变量。然后使用这个变量作为下一次调用array_append的输入参数。需要注意的是,一旦被库函数使用,id为[0]的变量就不能被重用,试图调用id为[0]的array_append作为输入参数会导致编译错误。
array _ new & lt毛毡& gt()-& gt;([0]);
毛毡_ const & lt1 & gt()-& gt;([1]);
存储温度& lt毛毡& gt([1])-& gt;([1]);
array_append<毛毡& gt([0],[1])-& gt;([2]);
毛毡_ const & lt1 & gt()-& gt;([4]);
存储温度& lt毛毡& gt([4])-& gt;([4]);
array_append<毛毡& gt([2]、[4])-& gt;([5]);
对于可以多次重复使用的物品,如毛毡,Sierra提供DUP
非故障断言通常使用断言来评估代码中特定点的布尔表达式的结果。如果评估结果不匹配,就会导致错误。与在Cairo 0中不同,Cairo 1断言指令的编译会生成分支Sierra代码。如果断言不满足,代码会提前终止当前函数的执行,继续执行下一条指令。
为了确保使用字典的程序的可靠性,字典和数组一样存在多次添加值的问题,这可以通过引入特殊的字典来解决
正如我们之前看到的,线性类型系统强制对象只能使用一次。“使用”Dict的唯一方法是调用dict_squash函数,该函数使用字典实例,不返回任何内容。这意味着在将Sierra代码编译成CA时会检测到未压缩的字典,并且在编译时会引发一个错误。对于其他不需要使用一次的类型,通常是那些不包含Dict的类型,Sierra引入了Drop
值得注意的是,drop和dup都不会生成任何CA代码。它们只在Sierra层提供类型安全,确保变量只使用一次。
防止无限循环决定程序最终停止或永远运行,这是计算机科学中的一个基本问题,称为关机问题,一般无法解决。在Starknet这样的分布式环境中,用户可以部署和运行任意代码,因此防止用户运行无限循环代码非常重要,比如下面的Cairo代码。
fn foo() { foo() }
因为如果停止条件从未满足,递归函数可能导致无限循环,所以Cairo-to-Sierra编译器将在递归函数的开头注入retract _ gas方法。因为这个函数还没有实现,所以开发人员仍然需要在递归函数中调用retract _ gas并自己处理结果,虽然在未来的版本中应该会包含在编译器中。
withdraw_Gas函数将通过计算函数中每个指令的运行成本,从事务的总可用气体中扣除运行函数所需的气体量。通过确定每个操作需要多少步骤来分析成本,并且大多数操作的步骤在编译时是已知的。在Cairo程序执行过程中,如果调用withdraw_gas返回null或负值,则当前函数的执行将停止,通过调用未压缩字典上的dict_squash和其他变量上的drop来消耗所有要处理的变量,这将被视为执行失败。由于Starknet上的所有事务都有有限数量的Gas可用于执行事务,因此避免了无限循环,并且通过确保仍有足够的Gas可用于删除变量和停止执行,定序器将能够从事务失败中收取费用。
通过有限的指令集实现安全CASierra的主要目的是保证生成的CA代码不会失败。为了实现这个目标,Sierra程序由调用libfuncs的语句组成。这些是一组内置的库函数,为这些函数生成的CA代码是安全的。例如,由array_append库函数生成的安全CA代码可用于向数组追加值。
这种通过只允许一组安全可靠的库函数来实现代码安全性的方法类似于Rust编程语言的哲学。通过提供一组安全可靠的抽象,这两种语言都有助于避免常见的编程错误,并提高代码的安全性和可靠性。Cairo 1使用了类似Rust的所有权和借用系统,为开发人员提供了一种在编译时推断代码安全性的方式,有助于防止错误和提高整体代码质量。
免责声明本文旨在为读者提供一般信息和理解。并不意味着尼日思维支持任何特定的资产、项目或团队,也不保证其安全性。对于本文所含信息或观点的准确性或完整性,尼瑟迈不做任何明示或暗示的陈述或保证。任何第三方不得以任何方式依赖本文,包括但不限于财务、投资、税务、监管、法律或其他建议,或将本文解释为任何形式的建议。请注意,虽然Nethermind为Starkware提供服务,但这篇文章不是这些服务的一部分。
本网站声明:网站内容来源于网络。如有侵权,请联系我们,我们会及时处理。
温馨提示:注:内容来源均采集于互联网,不要轻信任何,后果自负,本站不承担任何责任。若本站收录的信息无意侵犯了贵司版权,请给我们来信(j7hr0a@163.com),我们会及时处理和回复。
原文地址"cairo trilogy,cairo中文解释":http://www.guoyinggangguan.com/qkl/172547.html。
微信扫描二维码关注官方微信
▲长按图片识别二维码