本文介绍了如何使用操作码(Opcodes)编写、测试和部署智能合约。Opcodes是EVM可以理解的基本指令,直接使用Opcodes编写智能合约可以帮助开发者更深入地理解EVM的工作原理、优化Gas消耗、进行定制化功能开发和安全分析。文章通过一个简单的加法合约的示例,详细解释了如何将Opcodes转换为字节码,并通过Remix部署。
智能合约是区块链技术的基石。虽然许多开发者使用像 Solidity 这样的流行语言,但还有另一种编写智能合约的方式:使用 opcode。在这篇对初学者友好的指南中,我们将探讨如何仅使用 opcode 来编写、测试和部署智能合约。
Opcode,是操作码(operation codes)的缩写,是以太坊虚拟机 (EVM) 可以理解的基本指令。它们可能看起来很复杂,但它们提供了独特的优势:
直接使用 Opcode 可以深入了解 EVM 的运行方式,并为开发人员、研究人员和教育工作者提供有价值的见解。
Opcode 允许开发人员能够优化代码以提高 gas 效率——降低交易成本。
Opcode 允许实现高级语言可能无法实现的定制化和控制级别。开发人员可以根据其独特的需求来设计特定的功能。
通过检查合约的字节码,审计员和安全专家可以识别漏洞,并确保合约按预期运行。
使用 opcode 编写智能合约可能让人感到不知所措,但让我们分解任务,并学习如何使用 opcode 部署我们的第一个智能合约。
了解 opcode 是第一步。大约有 120 个 opcode,每个 opcode 都有不同的功能。以下是一些例子:
要了解更多关于 opcode 的信息,你可以查看这份综合指南。 练习使用 EVM playground 来熟悉 opcode。
现在,一旦你熟悉了 opcode——概述你的合约的逻辑。 确定你想要执行哪些函数,以及如何存储和操作数据。
在本指南中,我们将编写一个非常简单的 opcode 来求两个数字的和。
这是它的样子:
PUSH1 0X02
PUSH2 0X03
ADD
让我们简化这些行:
PUSH1 0x02
将值 0x02
推送到堆栈上PUSH1 0x03
将值 0x03
推送到堆栈上ADD
从堆栈中弹出顶部的两个值,将它们相加,然后将结果推回堆栈上一旦我们有了合约的 opcode。 让我们了解如何计算给定 opcode 的字节码:
PUSH1
PUSH1
的 opcode 是 0x60
0x02
要推送到堆栈上的值0x6002
PUSH1
:PUSH1
的 opcode 是 0x60
。0x03
:要推送到堆栈上的值。0x6003
。ADD
:ADD
的 opcode 是 0x01
。0x01
。将它们放在一起,给定 opcode 的完整字节码是:
0x6002600301
此字节码可用于部署或与以太坊网络上的合约进行交互。
Remix 没有提供直接编译原始 opcode 的方法。 因此,我们需要使用一个 wrapper 合约,其中将包含作为构造函数的字节码。 这将允许我们部署 opcode。
以下是我们的 wrapper 合约的样子:
pragma solidity ^0.8.0;
contract DeployOpcodes {
uint256 public sum;
constructor() {
deployOpcodes();
}
function deployOpcodes() private {
assembly {
let x := mload(0x40) // 查找空的存储位置
mstore(x, 0x02) // 第一个要添加的数字
mstore(add(x, 0x20), 0x03) // 第二个要添加的数字
let result := add(mload(x), mload(add(x, 0x20))) // 将数字相加
sstore(sum.slot, result) // 将结果存储在 "sum" 状态变量中
}
}
function getSum() public view returns (uint256) {
return sum;
}
}
在此合约中,我们有几个关键组件:
DeployOpcodes
:这是我们合约的名称,我们将在这里存放所有与 opcode 部署相关的函数和变量。deployOpcodes
:在这个函数中,我们将使用 assembly 语言直接与以太坊虚拟机 (EVM) 交互。 以下是每一行代码的分解:a. Assembly 代码块:我们以 assembly
关键字开头,允许我们编写底层 EVM 代码。
b. 查找空的存储位置:let x := mload(0x40)
这一行加载内存位置 0x40
处的值,0x40
是 EVM 中的一个特殊位置,通常包含 "空闲内存指针"。 通过将此值分配给变量 x
,我们找到一个空的存储位置。
c. 存储第一个数字:接下来,mstore(x, 0x02)
将值 0x02
存储在 x
指向的内存位置。 这表示我们想要添加的第一个数字。
d. 存储第二个数字:mstore(add(x, 0x20), 0x03)
这一行将值 0x03
存储在内存位置 x + 0x20
。 这表示要添加的第二个数字。
e. 计算总和:使用 let result := add(mload(x), mload(add(x, 0x20)))
,我们加载内存位置 x
和 x + 0x20
处的值,将它们相加,并将结果分配给一个名为 result
的变量。
f. 存储结果:最后,sstore(sum.slot, result)
将 result
的值存储在与 sum
状态变量对应的存储槽中。 这允许我们稍后检索总和。
现在使用 Remix 非常简单! 编译代码,然后点击 Deploy 按钮!
你可以看到我们有 getSum 函数,它将向我们返回数值 5。
这就是我们使用 opcode 编写智能合约的方式。
使用 opcode 编写智能合约不仅仅是一种学术练习; 对于想要优化、自定义和深入理解其智能合约的开发人员来说,它是一种强大的工具。
如果你觉得本指南有帮助,请鼓掌并关注我以获取更多见解!
Pari Tomar ( Twitter || LinkedIn)
- 原文链接: medium.com/@tomarpari90/...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!