本文讨论了EVM编译器如何为函数分派生成代码以及相关的风险,重点关注访问控制。编译器定义了智能合约中基本的访问控制机制。文章还探讨了函数分派的鸟瞰图,以及编译器可能出现的错误,例如不正确的可见性推断、遗漏中止指令、省略支付能力检查等,并强调了对编译器团队进行适当的资金支持以及在代码库中添加健全性检查测试的重要性。
本文讨论了EVM编译器如何为函数分发生成代码以及相关的风险,重点关注访问控制。
编译器定义了智能合约中基本的访问控制机制之一 - 哪些函数可以访问以及如何访问。
在像Vyper或Solidity这样的语言中,分发是隐式设置的,也就是说,它被编译器抽象出来,并在没有来自源代码的指令的情况下创建。
合约就像服务器,每次传入输入(外部交易)都需要启动。加载器(evm运行时)加载合约,合约从一个隐式的 main 函数开始执行。
main 函数负责解析来自 calldata 的输入,并执行与 calldata 中编码的用户命令相对应的功能。
这些命令具有由 abicoder v2 定义的特定结构(这种编码被所有主要的 evm 编译器采用)。该编码定义了如何编码执行合约函数的请求,即如何定义要执行哪个函数以及如何编码其输入。
函数是通过函数选择器选择的,函数选择器被定义为函数签名的 keccak256 的 4B (例如:keccak256("transfer(address,uint256)")[0:4]
)。
一旦从 calldata 加载了 4B,我们必须检查合约中是否存在相应的函数。这是如何完成的?
在语义分析期间,编译器会解析每个函数的可见性(私有/外部)。然后,在代码生成阶段,它会查看外部函数的集合并计算它们的函数选择器,这些选择器随后用于 main 函数中的函数分发。然后它可以构造 main 函数。
main 函数通常会执行以下操作:
if
链):if calldata[0:4] == selector_foo:
...
call foo
if calldata[0:4] == selctor_bar:
...
call bar
if fallback_defined:
...
call fallback
else:
abort execution
额外检查通常包括以下内容:
msg.value == 0
这与访问控制有什么关系? 让我们考虑一下可能出现的问题:
abort
并且代码继续执行会怎么样?如果其中任何一项失败,都可能产生严重的后果。如果公开了一个私有函数会怎么样? 所有私有函数都假定它们不能从顶层调用,因此不实现相应的访问控制。
假设所有这些点都已正确实现——这是否意味着访问控制已正确实现? 不,一般来说,编译器可能几乎具有任意的错误编译(编译成具有与原始源代码不同含义的字节码)。 为了确保正确实现访问控制,我们必须考虑代码中的所有可能路径,并确保它们都不会破坏源代码语义。
我们已经展示了如何在 EVM 编译器中构造函数分发,并讨论了相关的风险。在合约开发期间,我们经常认为很多事情是理所当然的。 调用约定或函数分发中的错误可能会对整个生态系统产生灾难性的后果。 为你的编译器团队提供适当的资金,并在你的代码库中添加一些健全性检查测试!
- 原文链接: hackmd.io/@cyberthirst/S...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!