Solidity 难学吗?
作为一门语言,学习 solidity 可以说是相对容易学习的语言之一。然而,学习 Ethereum 环境却很困难。
它看起来非常像 Javascript,或者几乎任何源自 C 的大括号语言。
If 语句、for 循环、类继承、变量类型等都非常令人熟悉。
Solidity 确实有一些在转移加密货币时独有的怪异之处。例如,每个函数调用都有一个环境变量,指示伴随该函数调用发送了多少 ether,并且它具有一些用于与其他智能合约交互的特定 API。Solidity 还有一些奇怪的指令,比如其他语言中找不到的 delegatecall 和 selfdestruct,但稍微琢磨一下文档后,这些都很容易掌握。
然而,Solidity 和 Ethereum 开发可能会充满意外。这里仅举三个例子。
表面上微小的变化可能会导致 gas 成本出现巨大差异

这个合约的作用非常简单。它将代币从另一个合约转移到自己这里,然后在同一笔交易中将其转移给调用者。
然而,openFaucetInefficient 和 openFaucetMoreEfficient 的 gas 成本可能会有天壤之别。为什么?在底层,ERC20 代币将用户的余额作为 storage 变量进行存储。当一个 storage 变量从零变为非零时,会导致该变量在区块链上被创建。而创建步骤伴随着很高的成本。当一个变量被设为零时,它会被隐式擦除。因此,第一个函数是在反复创建和擦除一个 storage 变量。
第二个函数要高效得多。它为自己的余额创建了 storage 变量,然后确保在最后转移走最后一笔代币之前该变量不会被销毁。这避免了 storage 变量的不必要创建。
第三个函数具有奇特的 for 循环结构,它的效率甚至更高。由于 Solidity 编译器的某些怪癖,以这种方式重新排列 for 循环会更加高效,即使你让编译器对代码进行自动效率优化也是如此。
你怎么会知道这些?说实话,并没有直接的方法可以知道。这就是为什么 Solidity 并不容易精通的原因。
I/O 操作可以被撤销

x 和 y 都是 storage 变量,你可以将它们视为存储在持久化磁盘上。它们在不同的交易之间会保留其值。
被问及此事时,许多开发者会假设 x 将被设置为 newValue,如果 x 小于或等于 10,则 y 将被设置为 x 的 2 倍。例如,如果 newValue 是 5,则 x 将为 5,y 将为 10。如果 newValue 是 20,则 x 将为 20,而 newValue 将保持不变。
但这并不是实际发生的情况。如果在一笔交易中的任何地方发生了 revert,对 X 的写入就会被撤销。
这对开发者来说非常反直觉,因为大多数 I/O 操作不会发生 revert。然而,Ethereum 上的所有交易都是原子性的。所以 x 永远不可能大于 10,并且 x 和 y 必须同步变化。
表面上无害的函数可能导致重入攻击

上面的代码似乎在人们调用 mintTokens 时向他们发送一个 ERC20 代币和一个 ERC1155 代币。表面上看,由于有了 alreadyClaimed 检查,每个地址只能铸造一次。
然而,有可能在单笔交易中抽干合约中的所有代币。ERC1155 中的 _mint 函数不仅会铸造代币,还会将控制权移交给交易的发送者——而且是在更新发送者已领取其代币的状态之前。这使得发送者可以在 _mint 将控制权交还给他们时,通过递归调用 mintTokens 来为自己领取所有的代币。
至于哪些函数会将控制权移交给其他合约,并没有什么规律或道理可言。人们只能靠死记硬背。
结论
正如你从上面看到的,一门看似简单的语言可能充满了意外。这里我们只是触及了皮毛。你知道“不可变的”(immutable)智能合约有可能改变其字节码吗?有些糟糕的代码设计允许买家在不进行重入攻击的情况下双花他们的 ether,你知道吗?尽管区块链是完全确定且透明的,你能安全地生成随机数吗?
尽管使用的是一门“易于”学习的编程语言,区块链依然充满了未知的未知因素。这就是为什么黑客攻击如此常见的原因。
Solidity 可以在一个周末内掌握。如果你已经了解了另一门编程语言,这里有我们免费的教程,可以让你快速 学习 Solidity。
但是要精通这个生态系统,绝不是几天之内就能做到的。
你想精通这个生态系统吗?现在就申请我们完全远程的 Solidity Bootcamp。
最初发布于 2022 年 11 月 8 日