解构智能合约:创造与运行时间代码解析

当前位置:首页 > 币圈百科 > 解构智能合约:创造与运行时间代码解析

解构智能合约:创造与运行时间代码解析

2022-12-22币圈百科244
为什么这段代码试图复制代码的最后32个字节?实际上,当部署带有包含参数的构造函数的协定时,参数作为原始十六进制数据附加到代码的末尾(向下滚动描述面板以查看此内容)。在这种情况下,构造函数接受一个uint256参数,因此这段代码所做的只是将附加在代码末尾的值中的参数复制到内存中。

这32条指令作为反汇编代码是没有意义的,但是用原来的十六进制表示:00000000000000000000000000000000000000000000000002710。当然,这是我们在部署智能合约时传递给构造函数的十进制值10000!

你可以一步一步地在Remix中重复这一部分,以确保你理解了刚刚发生的事情。最终结果应该是000的位置.002710.看到内存中的数字080。

好了,在我们开始下一部分之前,我建议喝杯威士忌休息一下。

威士忌时间!

为什么我建议你喝杯威士忌?因为从这里开始一切都在走下坡路。

下一组指令是29到35,将存储器地址0x40的值从0x80更新到0xa0。如您所见,它们将值偏移了0x20 (32)字节。

现在我们可以开始理解指令0到2了。Solidity跟踪所谓的“空内存指针”:即内存中我们可以用来存储东西的地方,确保没有人会覆盖它(除非我们出错)。因此,因为我们将数字10000存储在旧的空闲内存位置,所以我们通过向前移动32个字节来更新空闲内存指针。

即使是经验丰富的Solidity开发者,看到“空闲内存指针”或者代码mload(040,080)也会感到困惑。这些只是说,“每当我们写一个新的条目,我们会从这个点写入内存,并保留偏移记录”。

solidity中的每个函数,当编译成EVM字节码时,都会初始化这个指针。

000到040之间的内存是多少,你可能不知道。不是。Solidity保留的用于计算哈希值的内存区域。我们很快就会看到,这对于映射和其他类型的动态数据是必要的。

现在,在指令37中,MLOAD从内存中读取位置040,基本上将我们的值10000从内存中下载到堆栈中,在那里它将是新的,可以在下一组指令中使用。

这是Solidity生成的EVM字节码中的一个常见模式:在执行函数体之前,函数的参数被加载到堆栈中(只要有可能),以便即将到来的代码可以使用它们——这正是接下来将要发生的事情。

我们继续解释38到55。

图5。构造函数体的EVM代码。蓑衣网小编2022这些指令无非就是构造函数的体:也就是Solidity代码:

total supply _=_ initial supply;balances[msg . sender]=_ initial supply;

前四条指令非常明显(38到42)。首先将0压入栈中,然后复制栈中的第二个项(这是我们的10000号),然后将数字0复制压入栈中,这是位置slot totalSupply_ in存储。现在,SSTORE可以使用这些值,并且仍然保持在10000以下以备将来使用:

sstore (0x00,0x 2710)| | | whatstore。存放在哪里。(入库中)

看!我们将数字10000存储在变量totalSupply_中。是不是很神奇?

一定要在Remix的调试器标签中可视化这个值。您可以在储物空间满载的面板中找到它。

下一组指令(43到54)有点复杂,但基本上它将处理在balances映射中存储10000的键msg.sender。在继续之前,请确保您了解Solidity文档的这一部分,它解释了如何在内存中保存映射。

简而言之,它将把映射值的槽(在本例中是数字1,因为它是智能合约中声明的第二个变量)与使用的键(在本例中是msg.sender,通过操作码获取调用者)连接起来,然后用SHA3操作码提取抽象,并将其用作内存中的目标位置。最后,存储只是一个简单的字典或哈希表。

继续执行指令43至45,将msg.sender地址存储在内存中(此时在位置000),然后在指令46至50中,将值1(映射的槽)存储在内存位置020。最后,SHA3操作码计算从位置000到位置040的内存中的任何内容的Keccak256散列- 即映蓑衣网小编2022射的插槽/位置与所使用的键的串联。这正是值10000将存储在我们的映射中的位置:

sstore(hash.0x2710)| || What to store.Where to store.

此时,构造函数的主体已完全执行。

所有这些起初可能有点压倒性,但它是存储在Solidity中工作的基本部分。如果你没有得到它,我建议你跟着Remix的调试器重复几次,保持堆栈和内存面板。

另外,请随时提出以下问题。此模式在Solidity生成的EVM字节码中普遍使用,您将很快学会轻松识别它。最后,它只是计算在内存中保存映射的某个键的值的位置。

图6.运行时代码复制结构

在指令56至65中,我们再次执行代码复制。只有这一次,我们不会将代码的最后32个字节复制到内存中; 我们从位置00046(十进制70)开始复制0x01d1(十进制465)字节到位置0的内存。这是要复制的一大块代码!

如果您再次将滑块一直向右滑动,您将注意到位置70正好在我们的创建时EVM代码之后,执行停止的地方。运行时字节码包含在那些465个字节中。这是代码的一部分,它将作为智能合约的运行时代码保存在区块链中,该代码将是每次有人或某事与智能合约交互时执行的代码。(我们将在本系列的后续部分中介绍运行时代码)。

这正是指令66到69所做的:返回我们复制到内存的代码。

图7.运行时代码返回EVM字节码结构。

RETURN抓取复制到内存的代码并将其交给EVM。如果此创建代码在对00地址的事务的上下文中执行,则EVM将执行代码并将返回值存储为创建的智能合约的运行时代码。

到现在为止,我们的BasicToken代码将创建和部署智能合约实例,并准备蓑衣网小编2022好使用其初始状态和运行时代码。如果退后一步并查看图2,您将看到我们分析的所有EVM字节码结构都是通用的,除了以紫色突出显示的那个:也就是说,它们将是由Solidity编译器生成的创建时字节码。构造函数与构造函数的区别仅在于紫色部分 – 构造函数的实际体。获取嵌入在字节码末尾的参数的结构,以及复制运行时代码并将其返回的结构,可以被认为是样板代码和通用EVM操作码结构。您现在应该能够查看任何构造函数,在按指令学习之前,您应该对构成它的组件有一个大概的了解。

在本系列的下一篇文章中,我们将介绍实际的运行时代码,首先介绍怎么样在不同的入口点与智能合约的EVM代码进行交互。现在,给自己一个当之无愧的轻拍,因为你刚刚消化了系列中最困难的部分。你还应该具有强大的能力来读取和调试EVM字节码,理解通用结构,最重要的是,了解创建时和运行时EVM字节码之间的区别。这就是使得合约的构造函数在Solidity中如此特殊的原因。

我们将在下一篇的系列文章中继续解构!

*本文由Alejandro Santander首发于medium,由猎豹区块链安全翻译并整理*

解构智能合约:创造与运行时间代码解析 | 分享给朋友: