区块链智能合约
区块链智能合约
欢迎来到Blockchain方向!在CTF里,这个方向主要侧重于智能合约安全,题目的主要形式是提供题目合约,要求进行合约审计并且找到合约中的漏洞并利用它达成某一目的(比如修改solved变量),当然还有其它的类型,比如提供字节码然后进行EVM字节码逆向,然后再利用合约漏洞等。
目前智能合约的主流语言是Solidity
文章参考:
https://cauliweak9.github.io/2024/04/28/Blockchain-Guide/
靶场:
靶场的话先刷https://ethernaut.openzeppelin.com/
这个人有全wphttps://exp10it.io/2024/04/ethernaut-writeup/#puzzle-wallet
这个比较难 https://onlypwner.xyz/challenges 后面再刷
(一)准备工作:
1.钱包
1 | 1.MetaMask |
1 | 2.Phantom |
1 | 3.Sui Wallet |
2.编写代码IDE
编写Solidity合约最常使用的是Remix IDE,它支持本地的虚拟私链,同时可以利用Injected Provider和真实的链进行合约部署与交互
3.测试链
测试链Sepolia Testnet,它的链ID(Chain ID)是11155111
搜索ID就行
4.获取测试链的币
需要找到水龙头(Faucet)去接水,这个是黑话,其实就是获取某个链的代币,Faucet Link上面有很多不同的水龙头,你可以自行去接水,但是目前绝大部分的水龙头都需要你在主网上面有价值至少10USD的ETH,bruh…

5.区块链浏览器
1.如果在真实链上进行了合约交互,那么肯定需要一个区块链浏览器,这里推荐Etherscan
对于Sepolia测试链,将etherscan.io改成sepolia.etherscan.io即可(同理)
2.Ethplorer 也是推荐
(二)基础知识
(1)初识Web3
Web3解释:互联网的未来 | MetaMask Learn
深入探讨:
1.什么是 Web3?
https://consensys.io/blog/what-is-web3-here-are-some-ways-to-explain-it-to-a-friend
2.了解区块链和以太坊的基础知识(矿工和验证者、gas、加密货币和 NFT、区块浏览器、网络等)
(2)以太坊(Ethereum)
文章阅读
以下均参照该文档进行总结
以太坊简介
1.什么是区块链?
对区块链最好的描述是将其描述为一个公共数据库,它由网络中的许多计算机更新和共享。
Anders Brownworth 进行的这个演示(opens in a new tab)并观看下方的相关视频:
https://www.youtube.com/watch?v=_160oMzblY8
2.以太坊简介
以太坊是一条区块链,其中嵌入了计算机。 它是以去中心化、无需许可、抗审查的方式构建应用程序和组织的基础
3.什么是以太币?
以太币 (ETH) 是以太坊上的原生加密货币。 以太币的目的是允许计算市场化。
4.什么是智能合约?
实际上,参与者不会每次在以太坊虚拟机上请求计算时都编写新代码。 相反,应用程序开发者将程序(可重用的代码片段)上传到以太坊虚拟机状态,用户发出请求以使用不同参数执行这些代码片段。 我们将这些上传至网络并由网络执行的程序称为智能合约。
简单来说,你可以把智能合约想象成一种自动售货机:通过特定参数调用脚本后,如果满足某些特定条件,就会执行一些操作或计算。 例如,如果调用者将以太币发送给特定的接收者,简单的卖方智能合约就可以创建和分配数字资产所有权。
5.术语
1.区块链
所有在以太坊网络历史上提交给以太坊网络的区块的序列。 如此命名,是因为每个区块都包含对前一个区块的引用,这有助于保持所有区块的顺序,因而维持了精确历史记录的顺序。
2.ETH 以太币
以太币 (ETH) 是以太坊上的原生加密货币。 用户向其他用户支付以太币,让他们完成自己的代码执行请求。
3.EVM 以太坊虚拟机
以太坊虚拟机是一个全局虚拟计算机,以太坊网络中的每个参与者都会存储并一致同意其状态。 任何参与者都可以请求在以太坊虚拟机上执行任意代码;代码执行会改变以太坊虚拟机的状态。
4.Nodes 节点
存储以太坊虚拟机状态的实体计算机。 节点间相互通信,传播关于以太坊状态的信息及其新的状态变化。 任何用户还可以通过广播来自节点的代码执行请求来请求代码执行。 以太坊网络本身就是所有以太坊节点及其通信的集合。
5.帐户
帐户是存储以太币之处。 用户可以初始化帐户,将以太币存入帐户,并将自己帐户中的以太币转账给其他用户。 帐户和帐户余额存储在以太坊虚拟机中的一个大表格中,是以太坊虚拟机总体状态的一部分。
6.交易
“交易请求”是表示以太坊虚拟机上的代码执行请求的正式术语。“交易”是指已完成的交易请求和相关的以太坊虚拟机状态变化。 任何用户都可以从节点向网络广播交易请求。 为了使交易请求影响一致同意的以太坊虚拟机状态,就必须由其他节点对其进行验证、执行并“提交到网络”。 执行任何代码都会导致以太坊虚拟机状态变化;一旦提交后,该状态变化将广播到网络中的所有节点。 以下为一些交易示例:
- 从我的帐户发送 X 个以太币到 Alice 的帐户。
- 将一些智能合约代码发布到以太坊虚拟机状态中。
- 使用 Y 参数执行 EVM 中 X 地址的智能合约代码。
7.区块
交易量巨大,因此交易分批或分区块“提交”。 区块通常包含数十至数百笔交易。
8.智能合约
是指开发者发布到以太坊虚拟机状态中的可重用代码片段(程序)。 任何人都可以通过提出交易请求来请求执行智能合约代码。 因为开发者可以通过发布智能合约将任意可执行应用程序(游戏,市场,金融工具等)写入以太坊虚拟机,所以这些通常也称为dapp 或去中心化应用程序
6.相关教程——未完成
- 以太坊开发者指南,第 1 部分 _ – 探索以太坊(非常适合使用 Python 和 web3.py 的初学者)
以太币简介
1.什么是加密货币?
加密货币是一种基于分布式账本(区块链)的交换媒介。
交换媒介是指被广泛接受、可支付任何商品和服务的物品,而分类账是记录交易的数据存储系统。 区块链技术允许用户在分类账上进行交易,而不必依赖受信赖的第三方来维护分类账。
2.什么是以太币 (ETH)?
以太币 (ETH) 是用于以太坊网络上许多事物的加密货币。 从根本上讲,以太币是唯一可接受的交易费支付方式,并且在合并之后,在主网上验证和提出区块需要以太币。 以太币还被用作 去中心化金融 借贷市场的主要抵押形式,非同质化代币市场的主要记账单位以及提供服务、销售实体商品赚取的付款等。
以太坊允许开发者创建去中心化应用程序 (dapp),它们共享算力池。 这个共享池是有限的,因此以太坊需要一种机制来确定谁可以使用它。 否则,某个 dapp 可能会意外或恶意地消耗所有网络资源,从而导致其他应用程序无法访问算力池。
ETH 加密货币支持以太坊算力的定价机制。 当用户想要完成一笔交易时,他们必须支付以太币,使他们的交易被区块链识别。 这些使用成本被称为 gas 费用,gas 费用的多少取决于执行交易所需的算力和全网当时的算力需求。
因此,即使某恶意 dapp 无限循环提交,交易最终也会耗尽 ETH 并终止,从而使网络恢复正常。
3.铸造 ETH
铸造是指在以太坊分类账上创造新以太币的过程。 底层以太坊协议创造出新以太币,单一用户不可能创造。
以太币铸造出来,用来奖励提议的每个区块,以及在每个时段的检查点奖励验证者执行的和达成共识有关的其他活动。 总发行量取决于验证者的数量和它们质押的以太币数量。 在所有验证者都诚实且在线的理想情况下,以太币总发行量会在所有验证者中等分,但现实中分配情况会因验证者的表现而异。 总发行量的大约 1/8 会奖励给区块提议者,剩余部分在其它验证者中分配。 区块提议者还会获得交易费小费和矿工可提取价值,但这些都来自流通中的以太币,而非新发行的以太币。
4.燃烧 ETH
除了通过区块奖励创建以太币,以太币也能通过“燃烧”过程销毁。 当 ETH 被燃烧掉,它也就永久退出流通。
以太坊上的每一笔交易都会发生以太币销毁。 当用户为他们的交易支付费用时,网络根据交易需求设置的基础燃料费会被销毁。 以太币销毁再加上可变区块大小和最高燃料费,简化了以太坊上的交易费估算。 网络需求量高时,[区块(opens in a new tab)]燃烧的以太币数量可以多于铸造的以太币数量,有效地抵消了以太币的发行。
销毁基础费抑制区块生产者操纵交易的能力。 例如,如果区块生产者获得了基础费,他们可以免费添加自己的交易,并提高其他所有人的基础费。 或者,矿工可以将基础费退还给一些链下用户,造成交易费市场更加不透明和复杂。
5.ETH 面额
由于以太坊上很多交易数额都比较小,因此以太币有几种面额,可以作为较小的记账单位。 在这些面额中,Wei 与 Gwei 特别重要。
Wei 是最小的以太币面额,因此在[以太坊黄皮书(opens in a new tab)]等众多技术实现中,都以 Wei 为单位进行计算。
Gwei(giga-wei 的缩写),常用于描述以太坊上的燃料费用。
| 面额 | ETH 值 | 常见用法 |
|---|---|---|
| Wei | 10-18 | 技术实施 |
| Gwei | 10-9 | 可读燃料费用 |
6.传输 ETH
以太坊上的每笔交易都包含一个 value 字段,指定从发送者地址发送到接收者地址的以太币转账金额(以 Wei 为单位)。
当接收者地址是智能合约时,在智能合约执行其代码后,这些转账的以太币可用于支付燃料费用。
7.查询 ETH
用户可以通过检查帐户的 balance 字段来查询任何帐户的以太币余额,该字段显示以太币持有数量(以 Wei 为单位)。
Etherscan(opens in a new tab) 是一种常用工具,用于通过基于 Web 的应用程序检查地址余额。 例如,此 Etherscan 页面(opens in a new tab)显示以太坊基金会的余额。 也可以通过使用钱包或直接向节点提出请求来查询帐户余额。
8.换算工具(Wei,Gwei,ETH)
Gwei 计算器(opens in a new tab):使用这个 Gwei 计算器可以轻松地换算 wei、Gwei 和 ETH。 只需输入任何数量的 wei、Gwei 或 ETH,就能够自动换算
9.例子

当然这个是经过翻译的,可以看到gas费用,以及单位
去中心化应用程序简介
去中心化应用程序 (dapp) 是在去中心化网络上构建的应用程序,结合了智能合约和前端用户界面。 请注意,就像开放 API 一样,以太坊智能合约具有可访问性和透明性,所以你的 dapp 里甚至可以包含其他人写过的智能合约。
1.dapp 的定义
一个 dapp 的后端代码在一个去中心化 P2P 网络上运行。 与此相对应的,是在中心化服务器上运行后端代码的应用程序。
dapp 可以用任何语言编写(就像是一个 app)。它有前端代码和用户界面,能调用其后端。 此外,它的前端可以托管在去中心化存储上,例如 IPFS(opens in a new tab)。
- 去中心化 - dapp 在以太坊上运行,这是一个开放的公共去中心化平台,没有任何一个人或团体可以控制
- 确定性 ,无论执行的环境如何,都执行相同的功能
- 图灵完备 - dapp 可以根据所需资源执行任何操作
- 隔离性 - 它们在称为 EVM 的虚拟环境中执行。即使智能合约出现问题,也不会妨碍区块链网络的正常运行
2.用来创建去中心化应用程序的工具
Scaffold-ETH - 使用能自适应智能合约的前端,快速尝试 Solidity。
创建以太坊应用程序 - 使用一个命令创建以太坊驱动的应用程序。
一键去中心化 - 用于从 ABI 生成去中心化应用程序前端的免费和开放源代码软件工具。
Etherflow - 供以太坊开发者测试节点,并在浏览器中编写和调试 RPC 调用的免费和开放源代码软件工具。
thirdweb - 为 Web3 开发提供各种语言的软件开发工具包、智能合约、工具和基础设施。
Crossmint- 企业级 Web3 开发平台,可用于部署智能合约,支持信用卡和跨链支付方式,可利用应用程序接口创建、分发、销售、存储、编辑非同质化代币。
账户
帐户类型
以太坊有两种帐户类型:
外部所有的帐户 (EOA) – 由任何拥有私钥的人控制
合约帐户 – 部署到网络上的智能合约,由代码控制
这两种帐户类型都能:
- 接收、持有和发送 ETH 和 token
- 与已部署的智能合约进行交互
主要区别
外部持有
- 创建帐户是免费的
- 可以发起交易
- 外部所有的帐户之间只能进行以太币和代币交易
- 由一对加密密钥组成:控制帐户活动的公钥和私钥
合约
- 创建合约存在成本,因为需要使用网络存储空间
- 只能在收到交易时发送交易
- 从外部帐户向合约帐户发起的交易能触发可执行多种操作的代码,例如转移代币甚至创建新合约
- 合约帐户没有私钥。 相反,它们由智能合约代码逻辑控制
理解帐户
以太坊帐户有四个字段:
nonce- 一个计数器,用来显示外部帐户发送的交易数量或合约帐户创建的合约数量。 每个帐户只能执行具有一个给定随机数的一笔交易,以防范重放攻击,重放攻击指多次广播和重复执行已签署的交易。balance– 这个地址拥有的 Wei 数量。 Wei 是以太币的计数单位,每个 ETH 有 1e+18 个 Wei。codeHash- 该哈希表示以太坊虚拟机 (EVM) 上的帐户_代码_。 合约帐户具有编程的代码片段,可以执行不同的操作。 如果帐户收到消息调用,则执行此 EVM 代码。 与其他帐户字段不同,不能更改。 所有代码片段都被保存在状态数据库的相应哈希下,供后续检索。 此哈希值称为 codeHash。 对于外部所有的帐户,codeHash 字段是空字符串的哈希。storageRoot– 有时被称为存储哈希。 Merkle Patricia trie 根节点的 256 位哈希已编码了帐户的存储内容(256 位整数值映射),并编码为 Trie,作为来自 256 的 Keccak 256 位哈希的映射位整数键,用于 RLP 编码的256位整数值。 此 Trie 对此帐户存储内容的哈希进行编码,默认情况下为空。

外部持有的帐户和密钥对
帐户由一对加密密钥组成:公钥和私钥。 它们有助于证明交易实际上是由发送者签名的,并防止伪造。 你的私钥是你用来签名交易的密钥,所以它保障你对与自己帐户相关的资金进行管理。 你从未真正持有加密货币,你持有私钥 – 资金总是在以太坊的账本上。
这将防止恶意参与者广播虚假交易,因为你总是可以验证交易的发送者。
帐户创建
1.当你想要创建一个帐户时,大多数程序库会生成一个随机私钥。
私钥由 64 个十六进制字符组成,可以用密码加密保存。
例如:
1 | fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036415f |
使用椭圆曲线加密法(opens in a new tab)从私钥生成公钥。 通过获取公钥 Keccak-256 哈希的最后 20 个字节并校验码前面添加 0x,可以为帐户获取公共地址。
这意味着外部帐户 (EOA) 有一个 42 字符的地址( 20 字节区块,即 40 个十六进制字符,加上 0x 前缀)。
例如:
1 | 0x5e97870f263700f46aa00d967821199b9bc5a120 |
2.下面的示例显示如何使用一种名为 Clef(opens in a new tab) 的签名工具生成一个新帐户。 Clef 是一个集成在以太坊客户端 Geth(opens in a new tab) 中的帐户管理和签名工具。 clef newaccount 命令创建一个新的密钥对并保存在加密的密钥库中。
1 | > clef newaccount --keystore <path> |
可以通过你的私钥获取公钥,但你不能通过公钥获取私钥。 确保私钥的安全性,以及顾名思义,私密性,至关重要。
你需要一个私钥来签署消息和交易并输出签名。 然后其他人可以使用签名获取你的公钥,证明信息的作者。 在你的应用程序中,你可以使用 JavaScript 程序库向网络发送交易
合约帐户
合约帐户也有一个 42 个字符组成的十六进制地址:
例如:
1 | 0x06012c8cf97bead5deae237070f9587f8e7a266d |
合约地址通常在将合约部署到以太坊区块链时给出。 地址产生自创建人的地址和从创建人地址发送的交易数量(“nonce”)。
验证者密钥
以太坊还有一另种类型的密钥,它们是在以太坊从工作量证明过渡到权益证明共识时引入的。 它们是“BLS”密钥,用来识别验证者。 这些密钥可以有效地聚合,减少网络达成共识所需要的带宽。 没有这种密钥集合,验证者的最小质押金额将会高出许多。
关于钱包的说明
帐户和钱包不同。 钱包是一个界面或应用程序,可让你与以太坊帐户(外部帐户或合约帐户)进行交互。
延伸阅读——未完成
- 了解以太坊帐户(opens in a new tab) - etherscan
交易
交易是由帐户发出,带密码学签名的指令。 帐户将发起交易以更新以太坊网络的状态。 最简单的交易是将 ETH 从一个帐户转到另一个帐户。
什么是交易
以太坊交易是指由外部持有帐户发起的行动,换句话说,是指由人管理而不是智能合约管理的帐户。 例如,如果 Bob 发送 Alice 1 ETH,则 Bob 的帐户必须减少 1 ETH,而 Alice 的帐户必须增加 1 ETH。 交易会造成状态的改变。
改变 EVM 状态的交易需要广播到整个网络。 任何节点都可以广播在以太坊虚拟机上执行交易的请求;此后,验证者将执行交易并将由此产生的状态变化传播到网络的其他部分。
交易需要付费并且必须包含在一个有效区块中。 为了使本概述更加简洁,我们将另行介绍燃料费和验证。
所提交的交易包括下列信息:
from- 发送者的地址,该地址将签署交易。 这将是一个外部帐户,因为合约帐户无法发送交易to— 接收地址(如果是外部帐户,交易将传输值。 如果是合约帐户,交易将执行合约代码)signature– 发送者的标识符。 当发送者的私钥签署交易并确保发送者已授权此交易时,生成此签名。nonce- 一个有序递增的计数器,表示来自帐户的交易数量value– 发送者向接收者转移的以太币数量(面值为 WEI,1 个以太币 = 1e+18wei)input data– 可包括任意数据的可选字段gasLimit– 交易可以消耗的最大数量的燃料单位。 [以太坊虚拟机]指定每个计算步骤所需的燃料单位maxPriorityFeePerGas- 作为小费提供给验证者的已消耗燃料的最高价格maxFeePerGas- 愿意为交易支付的每单位燃料的最高费用(包括baseFeePerGas和maxPriorityFeePerGas)
燃料是指验证者处理交易所需的计算。 用户必须为此计算支付费用。 gasLimit 和 maxPriorityFeePerGas 决定支付给验证者的最高交易费。
交易对象看起来像这样:
1 | { |
但交易对象需要使用发送者的私钥签名。 这证明交易只可能来自发送者,而不是欺诈。
Geth 这样的以太坊客户端将处理此签名过程。
示例 JSON-RPC 调用:
1 | { |
示例响应:
1 | { |
raw是采用递归长度前缀 (RLP) 编码形式的签名交易tx是已签名交易的 JSON 形式。
如有签名哈希,可通过加密技术证明交易来自发送者并提交网络。
data字段
绝大多数交易都是从外部所有的帐户访问合约。 大多数合约用 Solidity 语言编写,并根据应用程序二进制接口 (ABI) 解释其data字段。
前四个字节使用函数名称和参数的哈希指定要调用的函数。 有时可以使用本数据库(opens in a new tab)根据选择器识别函数
调用数据的其余部分是参数,**按照应用程序二进制接口规范中的规定进行编码(合约ABI规范)**
例如,我们来看一下这笔交易(opens in a new tab)。 使用 Click to show More(单击查看更多)查看调用数据。
函数选择器是 0xa9059cbb。 有几个具有此签名的已知函数(opens in a new tab)(到数据库里查找ID)。 本例中合约源代码(opens in a new tab)已经上传到 Etherscan,所以我们知道该函数是 transfer(address, uint256)。
其余数据如下:
1 | 10000000000000000000000004f6742badb049791cd9a37ea913f2bac38d01279 |
根据应用程序二进制接口规范,整型值(例如地址,它是 20 字节整型)在应用程序二进制接口中显示为 32 字节的字,前面用零填充。 所以我们知道 to 地址是4f6742badb049791cd9a37ea913f2bac38d01279(opens in a new tab)。
value 是 0x3b0559f4 = 990206452。
交易类型
以太坊有几种不同类型的交易:
- 常规交易:从一个帐户到另一个帐户的交易。
- 合约部署交易:没有“to”地址的交易,数据字段用于合约代码。
- 执行合约:与已部署的智能合约进行交互的交易。 在这种情况下,“to”地址是智能合约地址。
关于燃料
如上所述,执行交易需要耗费燃料。 简单的转账交易需要 21000 单位燃料。
因此,如果 Bob 要在 baseFeePerGas 为 190 Gwei 且 maxPriorityFeePerGas 为 10 Gwei 时给 Alice 发送一个以太币,Bob 需要支付以下费用:
1 | (190 + 10) * 21000 = 4,200,000 gwei |
Bob 的帐户将会扣除 1.0042 个以太币(1 个以太币给 Alice,0.0042 个以太币作为燃料费用)
Alice 的帐户将会增加 +1.0 ETH
基础费将会燃烧 -0.00399 ETH
验证者获得 0.000210 个以太币的小费
任何未用于交易的燃料都会退还给用户帐户。
智能合约交互
任何涉及智能合约的交易都需要燃料。
智能合约还可以包含被称为 view(opens in a new tab) 或 pure(opens in a new tab) 的函数,这不会改变合约的状态。 像这样,从外部帐户调用这些函数不需要任何燃料。 此场景的底层远程过程调用 (RPC) 为 eth_call:
(立即执行新的消息调用,但不在区块链上创建交易。 通常用来执行只读智能合约的函数,例如 ERC-20 合约的 balanceOf 函数。)
不同于使用 eth_call 进行访问,view 或 pure 函数通常也在内部(即从合约自身或其他合约)调用并消耗燃料。
交易生命周期
交易提交后,就会发生以下情况:
- 以加密方式生成的交易哈希:
0x97d99bc7729211111a21b12c933c949d4f31684f1d6954ff477d0477538ff017 - 然后,该交易被广播到网络,并添加到由所有其他待处理的网络交易组成的交易池中。
- 验证者必须选择你的交易并将它包含在一个区块中,以便验证交易并认为它“成功”。
- 随着时间的流逝,包含你的交易的区块将升级成“合理”状态,然后变成“最后确定”状态。 通过这些升级,可以进一步确定 你的交易已经成功并将无法更改。 区块一旦“最终确定”,只能通过耗费数十亿美元 的网络级攻击来更改。
Typed Transaction Envelope交易
以太坊最初有一种交易形式。 每笔交易都包含 Nonce、燃料价格、燃料限制、目的地地址、价值、数据、v、r 和 s。 这些字段为 RLP 编码,看起来像这样:
1 | RLP([nonce, gasPrice, gasLimit, to, value, data, v, r, s]) |
以太坊经过演变,已经支持多种类型的交易,从而能够在不影响传统交易形式的情况下实现访问列表和 EIP-1559(opens in a new tab) 等新功能
**EIP-2718(opens in a new tab).**是允许这种行为的。 交易解释如下:
1 | TransactionType || TransactionPayload |
其中,字段定义如下:
TransactionType- 一个在 0 到 0x7f 之间的数字,总共为 128 种可能的交易类型。TransactionPayload- 由交易类型定义的任意字节数组。
基于 TransactionType 值,交易可被分为以下几类:
- Type 0(传统)交易:自以太坊推出以来使用的原始交易格式。 它们不包含 EIP-1559(opens in a new tab) 中的功能,例如动态燃料费计算或智能合约访问列表。 传统交易缺少以序列化形式表明其类型的特定前缀,在使用递归长度前缀编码 (RLP) 时以
0xf8字节开头。 这些交易的 TransactionType 值为0x0。 - Type 1 交易: 作为以太坊柏林升级的一部分在 EIP-2930(opens in a new tab) 中引入,这些交易包含一个
accessList参数。 该列表指定了交易预期访问的地址和存储密钥,有助于降低涉及智能合约的复杂交易的潜在燃料花费。 EIP-1559 的费用市场变化不包含在 Type 1 交易中。 Type 1 交易还包括一个yParity参数,它可以是0x0或0x1,表示 secp256k1 签名的 y 值奇偶性。 它们以字节0x01开头进行标识,其交易类型 (TransactionType) 值为0x1。 - Type 2 交易通常称为 EIP-1559 交易,是在以太坊伦敦升级的 EIP-1559(opens in a new tab) 中引入的。 它们已成为以太坊网络上的标准交易类型。 这些交易引入了一种新的费用市场机制,通过将交易费分为基础费用和优先费用来提高可预测性。 它们以字节
0x02开头,并包括maxPriorityFeePerGas和maxFeePerGas的字段。 Type 2 交易因其灵活性和效率,现已成为默认选择,特别是在网络严重拥堵期间,由于它能够帮助用户提高管理交易费用的可预测性,因此特别受到青睐。 这些交易的 TransactionType 值为0x2。
延伸阅读
区块
区块是指一批交易的组合,并且包含链中上一个区块的哈希。 这将区块连接在一起(成为一个链),因为哈希是从区块数据中加密得出的。 这可以防止欺诈,因为以前的任何区块中的任何改变都会使后续所有区块无效,而且所有哈希都会改变,所有运行区块链的人都会注意到。
为什么要有区块?
为了确保以太坊网络上的所有参与者保持同步状态并就交易的确切历史达成共识,我们将交易分为多个区块。 这意味着同时有数十个(甚至数百个)交易被提交、达成一致并同步。
通过间隔提交,所有网络参与者有足够时间达成共识:即使交易请求每秒发生数十次,但以太坊上的区块仅仅大约每十二秒创建并提交一次。
区块如何工作
为了保存交易历史,区块被严格排序(创建的每个新区块都包含一个其父块的引用),区块内的交易也严格排序。 除极少数情况外,在任何特定时间,网络上的所有参与者都同意区块的确切数目和历史, 并且正在努力将当前的活动交易请求分批到下一个区块。
随机选择的验证者在网络上构建完区块后,该区块将传播到整个网络;所有节点都将该区块添加至其区块链的末尾,然后挑选新的验证者来创建下一个区块。 目前,确切的区块构建过程和提交/共识过程由以太坊的“权益证明”协议规定。
权益证明协议
权益证明是指:
- 验证节点必须向存款合约中质押 32 个以太币,作为抵押品防止发生不良行为。 这有助于保护网络,因为如果发生不诚实活动且可以证实,部分甚至全部质押金额将被销毁。
- 在每个时隙(12 秒的时间间隔)中,会随机选择一个验证者作为区块提议者。 他们将交易打包并执行,然后确定一个新的“状态”。 他们将这些信息包装到一个区块中并传送给其他验证者。
- 其他获悉新区块的验证者再次执行区块中包含的交易,确定他们同意对全局状态提出的修改。 假设该区块是有效的,验证者就将该区块添加进各自的数据库。
- 如果验证者获悉在同一时隙内有两个冲突区块,他们会使用自己的分叉选择算法选择获得最多质押以太币支持的那一个区块。
区块包含什么?
一个区块中包含很多信息。 区块的最高层包含以下字段:
| 字段 | 简介 |
|---|---|
时隙 |
区块所属的时隙 |
proposer_index |
提出区块的验证者的 ID |
parent_root |
上一个区块的哈希 |
state_root |
状态对象的根哈希 |
正文 |
包含多个字段的对象,定义如下 |
区块的 body 包含一些自有字段:
| 栏目 | 简介 |
|---|---|
randao_reveal |
用于选择下一个区块提议者的值 |
eth1_data |
关于存款合约的信息 |
涂鸦 |
用于标记区块的任意数据 |
proposer_slashings |
将要惩罚的验证者列表 |
attester_slashings |
将要受到惩没的证明者列表 |
认证 |
支持当前区块的认证列表 |
存款 |
存款合约新增存款的列表 |
voluntary_exits |
退出网络的验证者列表 |
sync_aggregate |
用于服务轻客户端的验证者子集 |
execution_payload |
从执行客户端传送来的交易 |
attestations 字段包含区块中所有认证的列表。 认证有自己的数据类型,其中包含多条数据。 每个认证包含:
| 栏目 | 简介 |
|---|---|
aggregation_bits |
参与此认证的验证者列表 |
数据 |
具有多个子字段的容器 |
签名 |
所有证明验证者的聚合签名 |
attestation 中的 data 字段包含以下内容:
| 栏目 | 简介 |
|---|---|
时隙 |
认证所涉及的时隙 |
索引 |
证明验证者的索引 |
beacon_block_root |
包含此对象的信标区块的根哈希 |
来源 |
最后一个合理的检查点 |
target |
最新的时段边界区块 |
执行 execution_payload 中的交易会更新全局状态。 所有客户端重新执行 execution_payload 中的交易,以确保新状态与新区块 state_root 字段中的状态相符。 这就是客户端如何判断新区块是否有效且可以安全添加到其区块链的方式。 execution payload 本身是一个包含多个字段的对象。 还有一个 execution_payload_header,包含有关执行数据的重要摘要信息。 这些数据结构如下组织:
execution_payload_header 包含以下字段:
| 领域 | 简介 |
|---|---|
父_哈希值 |
父块的哈希值 |
fee_recipient |
向其支付交易费的帐户地址 |
state_root |
应用此区块中的更改后,全局状态的根哈希 |
receipts_root |
交易收据树的哈希 |
logs_bloom |
包含事件日志的数据结构 |
prev_randao |
在随机选择验证者时使用的值 |
block_number |
当前区块的编号 |
gas_limit |
此区块允许的最大燃料量 |
gas_used |
此区块中使用的实际燃料量 |
时间戳 |
区块时间 |
extra_data |
作为原始字节的任意附加数据 |
base_fee_per_gas |
基础费值 |
block_hash |
执行区块的哈希 |
transactions_root |
有效载荷中交易的根哈希 |
withdrawal_root |
有效负载中提款的根哈希 |
execution_payload 本身包含以下字段(请注意这与 header 相同,只是它包含的不是交易的根哈希,而是实际的交易列表和提款信息列表):
| 栏目 | 简介 |
|---|---|
父_哈希值 |
父块的哈希值 |
fee_recipient |
支付交易费用的帐户地址 |
state_root |
应用此区块中的更改后,全局的根哈希值 |
receipts_root |
交易收据的哈希值 |
logs_bloom |
包含事件日志的数据结构 |
prev_randao |
在随机验证者选择中使用的值 |
block_number |
当前区块的编号 |
gas_limit |
此区块允许的最大的燃料量 |
gas_used |
此区块中使用的燃料实际量 |
时间戳 |
区块时间 |
extra_data |
作为原始字节任意附加数据 |
base_fee_per_gas |
基本费用值 |
block_hash |
执行区块的哈希值 |
交易 |
要执行交易的列表 |
提款 |
提款对象列表 |
Withdrawals 列表包含了 withdrawal 对象,结构如下:
| 字段 | 描述 |
|---|---|
地址 |
提款帐户地址 |
amount |
提款金额 |
索引 |
提款索引值 |
validatorIndex |
验证者索引值 |
区块时间
区块时间是指两个区块之间的时间间隔。 在以太坊中,时间划分为每 12 秒一个单位,称为“时隙”。 在每个时隙内,选择一个单独的验证者提议区块。 假设所有验证者都在线且完全正常运行,则每个时隙内都会有一个区块产生,意味着区块时间是 12 秒。 但是,偶尔验证者在被要求提议区块时不在线,导致有时候一些时隙是空的。
这种实现与基于工作量证明的系统不同。在工作量证明系统中,区块时间是带有概率性的,并由协议的目标挖矿难度调节。 以太坊的平均区块时间(opens in a new tab)是一个很好的例子,根据不变的新的 12 秒区块时间,可以清楚地推断出从工作量证明到权益证明的过渡。
区块大小
最后一条重要提示是,区块本身的大小是有界限的。 每个区块的目标大小为 1500 万单位燃料,但区块的大小将根据网络需求而增加或减少,直至达到 3000 万单位燃料的区块限制(目标区块大小的 2 倍)。 区块的燃料限制可以相对于上一个区块的燃料限制上调或下调 1/1024 的比例。 因此,验证者可以通过共识来改变区块的燃料限制。 区块中所有交易消耗的总燃料量必须低于区块的燃料限制。 这很重要,因为它可以确保区块不能任意扩大。 如果区块可以任意扩大,由于空间和速度方面的要求,性能较差的全节点将逐渐无法跟上网络。 区块越大,在下一个时隙中及时处理它们需要的算力就越强大。 这是一种集中化的因素,可以通过限制区块大小来抵制。
以太坊虚拟机 (EVM)
以太坊虚拟机 (EVM) 是一个去中心化虚拟环境,它在所有以太坊节点上一种安全一致地方式执行代码。 节点运行以太坊虚拟机,以执行智能合约,利用“燃料”度量执行操作所需的计算工作,从而确保高效的资源分配和网络安全性。
前提条件
对计算机科学中常见术语的基本了解,如字节(opens in a new tab)、内存(opens in a new tab)和堆栈(opens in a new tab)是理解 EVM 的前提。 熟悉哈希函数(opens in a new tab)和默克尔树(opens in a new tab)等密码学/区块链概念也会很有帮助
从账本到状态机
通常使用“分布式账本”的类比来描述像比特币这样的区块链,它使用密码学的基本工具来实现去中心化的货币。 账本保存着活动记录,而活动必须遵守一套规则,这些规则限制用户在修改账本时可以做什么和不可以做什么。 例如,比特币地址不能花费比之前收到的更多的比特币。 这些规则是比特币和许多其他区块链上所有交易的基础。
虽然以太坊有自己的本机加密货币 (ETH),遵循几乎完全相同的直观规则,但它也支持更强大的功能:智能合约。 对于此更复杂的功能,需要一个更复杂的类比。 以太坊不是分布式账本,而是分布式状态机器(opens in a new tab)。 以太坊的状态是一个大型数据结构,它不仅保存所有帐户和余额,而且还保存一个_机器状态_,它可以根据预定义的一组规则在不同的区块之间进行更改,并且可以执行任意的机器代码。 在区块中更改状态的具体规则由 EVM 定义。
以太坊状态转换函数
EVM 的行为就像一个数学函数:在给定输入的情况下,它会产生确定性的输出。 因此,将以太坊更正式地描述为具有状态转换函数非常有帮助:
1 | Y(S, T)= S' |
给定一个旧的有效状态 (S)> 和一组新的有效交易 (T),以太坊状态转换函数 Y(S,T) 产生新的有效输出状态 S'
交易
交易是来自帐户的密码学签名指令。 交易分为两种:一种是消息调用交易,另一种是合约创建交易。
合约创建交易会创建一个新的合约帐户,其中包含已编译的 智能合约 字节码。 每当另一个帐户对该合约进行消息调用时,它都会执行其字节码。
EVM 说明
EVM 作为一个堆栈机(opens in a new tab)运行,其栈的深度为 1024 个项。 每个项目都是 256 位字,为了便于使用,选择了 256 位加密技术(如 Keccak-256 哈希或 secp256k1 签名)。
在执行期间,EVM 会维护一个瞬态_内存_(作为字可寻址的字节数组),该内存不会在交易之间持久存在。
然而,合约确实包含一个 Merkle Patricia 存储 trie(作为可字寻址的字数组),该 trie 与帐户和部分全局状态关联。
已编译的智能合约字节码作为许多 EVM opcodes执行,它们执行标准的堆栈操作,例如 XOR、AND、ADD、SUB等。 EVM 还实现了一些区块链特定的堆栈操作,如 ADDRESS、BALANCE、BLOCKHASH 等。
EVM 实现(重要)
EVM 的所有实现都必须遵守以太坊黄皮书中描述的规范。
在以太坊九年的历程中,以太坊虚拟机经过了几次修订,并且存在不同编程语言实现的以太坊虚拟机版本。
以太坊执行客户端都包含一个以太坊虚拟机实现。 此外,还有多个独立的实现,包括:
- Py-EVM(opens in a new tab) - Python
- evmone(opens in a new tab) - C++
- ethereumjs-vm(opens in a new tab) - JavaScript
- revm(opens in a new tab) - Rust
延伸阅读——未完成
- 以太坊黄皮书(opens in a new tab)
- Jellopaper aka KEVM:K 中的 EVM 语法(opens in a new tab)
- The Beigepaper(opens in a new tab)
- 以太坊虚拟机操作码(opens in a new tab)
- 以太坊虚拟机操作码交互参考(opens in a new tab)
- Solidity 文档的简短介绍(opens in a new tab)
- 精通以太坊 - 以太坊虚拟机(opens in a new tab)
以太坊虚拟机的操作码
在寻找交互式参考? 请查看 evm.codes(opens in a new tab)。
对于具有动态燃料成本的操作,请参考 gas.md(opens in a new tab)。
| 堆栈 | 名称 | 燃料 | 起始堆栈 | 最终堆栈 | 内存 / 存储 | 注释 |
|---|---|---|---|---|---|---|
| 00 | 停止 | 0 | halt execution | |||
| 01 | ADD | 3 | a, b |
a + b |
(u)int256 addition modulo 2**256 | |
| 02 | MUL | 5 | a, b |
a * b |
(u)int256 multiplication modulo 2**256 | |
| 03 | SUB | 3 | a, b |
a - b |
(u)int256 addition modulo 2**256 | |
| 04 | DIV | 5 | a, b |
a // b |
uint256 division | |
| 05 | SDIV | 5 | a, b |
a // b |
int256 division | |
| 06 | MOD | 5 | a, b |
a % b |
uint256 modulus | |
| 07 | SMOD | 5 | a, b |
a % b |
int256 modulus | |
| 08 | ADDMOD | 8 | a, b, N |
(a + b) % N |
(u)int256 addition modulo N | |
| 09 | MULMOD | 8 | a, b, N |
(a * b) % N |
(u)int256 multiplication modulo N | |
| 0A | EXP | A1(opens in a new tab) | a, b |
a ** b |
uint256 exponentiation modulo 2**256 | |
| 0B | SIGNEXTEND | 5 | b, x |
SIGNEXTEND(x, b) |
sign extend(opens in a new tab) x from (b+1) bytes to 32 bytes |
|
| 0C-0F | invalid | |||||
| 10 | LT | 3 | a, b |
a < b |
uint256 less-than | |
| 11 | GT | 3 | a, b |
a > b |
uint256 greater-than | |
| 12 | SLT | 3 | a, b |
a < b |
int256 less-than | |
| 13 | SGT | 3 | a, b |
a > b |
int256 greater-than | |
| 14 | EQ | 3 | a, b |
a == b |
(u)int256 equality | |
| 15 | ISZERO | 3 | a |
a == 0 |
(u)int256 iszero | |
| 16 | AND | 3 | a, b |
a && b |
bitwise AND | |
| 17 | OR | 3 | a, b |
`a | b` | |
| 18 | XOR | 3 | a, b |
a ^ b |
bitwise XOR | |
| 19 | NOT | 3 | a |
~a |
bitwise NOT | |
| 1A | BYTE | 3 | i, x |
(x >> (248 - i * 8)) && 0xFF |
ith byte of (u)int256 x, from the left |
|
| 1B | SHL | 3 | shift, val |
val << shift |
shift left | |
| 1C | SHR | 3 | shift, val |
val >> shift |
logical shift right | |
| 1D | SAR | 3 | shift, val |
val >> shift |
arithmetic shift right | |
| 1E-1F | invalid | |||||
| 20 | KECCAK256 | A2(opens in a new tab) | ost, len |
keccak256(mem[ost:ost+len-1]) |
keccak256 | |
| 21-2F | invalid | |||||
| 30 | ADDRESS | 2 | . |
address(this) |
address of executing contract | |
| 31 | BALANCE | A5(opens in a new tab) | addr |
addr.balance |
balance, in wei | |
| 32 | ORIGIN | 2 | . |
tx.origin |
address that originated the tx | |
| 33 | CALLER | 2 | . |
msg.sender |
address of msg sender | |
| 34 | CALLVALUE | 2 | . |
msg.value |
msg value, in wei | |
| 35 | CALLDATALOAD | 3 | idx |
msg.data[idx:idx+32] |
read word from msg data at index idx |
|
| 36 | CALLDATASIZE | 2 | . |
len(msg.data) |
length of msg data, in bytes | |
| 37 | CALLDATACOPY | A3(opens in a new tab) | dstOst, ost, len |
. |
mem[dstOst:dstOst+len-1] := msg.data[ost:ost+len-1] | copy msg data |
| 38 | CODESIZE | 2 | . |
len(this.code) |
length of executing contract’s code, in bytes | |
| 39 | CODECOPY | A3(opens in a new tab) | dstOst, ost, len |
. |
mem[dstOst:dstOst+len-1] := this.code[ost:ost+len-1] | |
| 3A | GASPRICE | 2 | . |
tx.gasprice |
gas price of tx, in wei per unit gas **(opens in a new tab) | |
| 3B | EXTCODESIZE | A5(opens in a new tab) | addr |
len(addr.code) |
size of code at addr, in bytes | |
| 3C | EXTCODECOPY | A4(opens in a new tab) | addr, dstOst, ost, len |
. |
mem[dstOst:dstOst+len-1] := addr.code[ost:ost+len-1] | copy code from addr |
| 3D | RETURNDATASIZE | 2 | . |
size |
size of returned data from last external call, in bytes | |
| 3E | RETURNDATACOPY | A3(opens in a new tab) | dstOst, ost, len |
. |
mem[dstOst:dstOst+len-1] := returndata[ost:ost+len-1] | copy returned data from last external call |
| 3F | EXTCODEHASH | A5(opens in a new tab) | addr |
哈希值 |
hash = addr.exists ? keccak256(addr.code) : 0 | |
| 40 | BLOCKHASH | 20 | blockNum |
blockHash(blockNum) |
||
| 41 | COINBASE | 2 | . |
block.coinbase |
当前区块提议者地址 | |
| 42 | TIMESTAMP | 2 | . |
block.timestamp |
timestamp of current block | |
| 43 | NUMBER | 2 | . |
block.number |
number of current block | |
| 44 | PREVRANDAO | 2 | . |
randomness beacon |
randomness beacon | |
| 45 | GASLIMIT | 2 | . |
block.gaslimit |
gas limit of current block | |
| 46 | CHAINID | 2 | . |
chain_id |
push current chain id(opens in a new tab) onto stack | |
| 47 | SELFBALANCE | 5 | . |
address(this).balance |
balance of executing contract, in wei | |
| 48 | BASEFEE | 2 | . |
block.basefee |
base fee of current block | |
| 49 | BLOBHASH | 3 | idx |
tx.blob_versioned_hashes[idx] |
EIP-4844(opens in a new tab) | |
| 4A | BLOBBASEFEE | 2 | . |
block.blobbasefee |
当前区块的二进制大对象基础费(EIP-7516(opens in a new tab)) | |
| 4B-4F | invalid | |||||
| 50 | POP | 2 | _anon |
. |
remove item from top of stack and discard it | |
| 51 | MLOAD | 3*(opens in a new tab) | ost |
mem[ost:ost+32] |
read word from memory at offset ost |
|
| 52 | MSTORE | 3*(opens in a new tab) | ost, val |
. |
mem[ost:ost+32] := val | write a word to memory |
| 53 | MSTORE8 | 3*(opens in a new tab) | ost, val |
. |
mem[ost] := val && 0xFF | write a single byte to memory |
| 54 | SLOAD | A6(opens in a new tab) | key |
storage[key] |
read word from storage | |
| 55 | SSTORE | A7(opens in a new tab) | key, val |
. |
storage[key] := val | write word to storage |
| 56 | JUMP | 8 | dst |
. |
$pc := dst mark that pc is only assigned if dst is a valid jumpdest |
|
| 57 | JUMPI | 10 | dst, condition |
. |
$pc := condition ? dst : $pc + 1 |
|
| 58 | PC | 2 | . |
$pc |
program counter | |
| 59 | MSIZE | 2 | . |
len(mem) |
size of memory in current execution context, in bytes | |
| 5A | GAS | 2 | . |
gasRemaining |
||
| 5B | JUMPDEST | 1 | mark valid jump destination | a valid jump destination for example a jump destination not inside the push data | ||
| 5C | TLOAD | 100 | key |
tstorage[key] |
从瞬态存储中读取字 (EIP-1153(opens in a new tab)) | |
| 5D | TSTORE | 100 | key, val |
. |
tstorage[key] := val | 向瞬态存储中写入字 (EIP-1153(opens in a new tab)) |
| 5E | MCOPY | 3+3*字+A0(opens in a new tab) | dstOst, ost, len |
. |
mem[dstOst] := mem[ost:ost+len] | 将内存从一个区域复制到另一个区域 (EIP-5656(opens in a new tab)) |
| 5F | PUSH0 | 2 | . |
uint8 |
push the constant value 0 onto stack | |
| 60 | PUSH1 | 3 | . |
uint8 |
push 1-byte value onto stack | |
| 61 | PUSH2 | 3 | . |
uint16 |
push 2-byte value onto stack | |
| 62 | PUSH3 | 3 | . |
uint24 |
push 3-byte value onto stack | |
| 63 | PUSH4 | 3 | . |
uint32 |
push 4-byte value onto stack | |
| 64 | PUSH5 | 3 | . |
uint40 |
push 5-byte value onto stack | |
| 65 | PUSH6 | 3 | . |
uint48 |
push 6-byte value onto stack | |
| 66 | PUSH7 | 3 | . |
uint56 |
push 7-byte value onto stack | |
| 67 | PUSH8 | 3 | . |
uint64 |
push 8-byte value onto stack | |
| 68 | PUSH9 | 3 | . |
uint72 |
push 9-byte value onto stack | |
| 69 | PUSH10 | 3 | . |
uint80 |
push 10-byte value onto stack | |
| 6A | PUSH11 | 3 | . |
uint88 |
push 11-byte value onto stack | |
| 6B | PUSH12 | 3 | . |
uint96 |
push 12-byte value onto stack | |
| 6C | PUSH13 | 3 | . |
uint104 |
push 13-byte value onto stack | |
| 6D | PUSH14 | 3 | . |
uint112 |
push 14-byte value onto stack | |
| 6E | PUSH15 | 3 | . |
uint120 |
push 15-byte value onto stack | |
| 6F | PUSH16 | 3 | . |
uint128 |
push 16-byte value onto stack | |
| 70 | PUSH17 | 3 | . |
uint136 |
push 17-byte value onto stack | |
| 71 | PUSH18 | 3 | . |
uint144 |
push 18-byte value onto stack | |
| 72 | PUSH19 | 3 | . |
uint152 |
push 19-byte value onto stack | |
| 73 | PUSH20 | 3 | . |
uint160 |
push 20-byte value onto stack | |
| 74 | PUSH21 | 3 | . |
uint168 |
push 21-byte value onto stack | |
| 75 | PUSH22 | 3 | . |
uint176 |
push 22-byte value onto stack | |
| 76 | PUSH23 | 3 | . |
uint184 |
push 23-byte value onto stack | |
| 77 | PUSH24 | 3 | . |
uint192 |
push 24-byte value onto stack | |
| 78 | PUSH25 | 3 | . |
uint200 |
push 25-byte value onto stack | |
| 79 | PUSH26 | 3 | . |
uint208 |
push 26-byte value onto stack | |
| 7A | PUSH27 | 3 | . |
uint216 |
push 27-byte value onto stack | |
| 7B | PUSH28 | 3 | . |
uint224 |
push 28-byte value onto stack | |
| 7C | PUSH29 | 3 | . |
uint232 |
push 29-byte value onto stack | |
| 7D | PUSH30 | 3 | . |
uint240 |
push 30-byte value onto stack | |
| 7E | PUSH31 | 3 | . |
uint248 |
push 31-byte value onto stack | |
| 7F | PUSH32 | 3 | . |
uint256 |
push 32-byte value onto stack | |
| 80 | DUP1 | 3 | a |
a, a |
clone 1st value on stack | |
| 81 | DUP2 | 3 | _, a |
a, _, a |
clone 2nd value on stack | |
| 82 | DUP3 | 3 | _, _, a |
a, _, _, a |
clone 3rd value on stack | |
| 83 | DUP4 | 3 | _, _, _, a |
a, _, _, _, a |
clone 4th value on stack | |
| 84 | DUP5 | 3 | ..., a |
a, ..., a |
clone 5th value on stack | |
| 85 | DUP6 | 3 | ..., a |
a, ..., a |
clone 6th value on stack | |
| 86 | DUP7 | 3 | ..., a |
a, ..., a |
clone 7th value on stack | |
| 87 | DUP8 | 3 | ..., a |
a, ..., a |
clone 8th value on stack | |
| 88 | DUP9 | 3 | ..., a |
a, ..., a |
clone 9th value on stack | |
| 89 | DUP10 | 3 | ..., a |
a, ..., a |
clone 10th value on stack | |
| 8A | DUP11 | 3 | ..., a |
a, ..., a |
clone 11th value on stack | |
| 8B | DUP12 | 3 | ..., a |
a, ..., a |
clone 12th value on stack | |
| 8C | DUP13 | 3 | ..., a |
a, ..., a |
clone 13th value on stack | |
| 8D | DUP14 | 3 | ..., a |
a, ..., a |
clone 14th value on stack | |
| 8E | DUP15 | 3 | ..., a |
a, ..., a |
clone 15th value on stack | |
| 8F | DUP16 | 3 | ..., a |
a, ..., a |
clone 16th value on stack | |
| 90 | SWAP1 | 3 | a, b |
b, a |
||
| 91 | SWAP2 | 3 | a, _, b |
b, _, a |
||
| 92 | SWAP3 | 3 | a, _, _, b |
b, _, _, a |
||
| 93 | SWAP4 | 3 | a, _, _, _, b |
b, _, _, _, a |
||
| 94 | SWAP5 | 3 | a, ..., b |
b, ..., a |
||
| 95 | SWAP6 | 3 | a, ..., b |
b, ..., a |
||
| 96 | SWAP7 | 3 | a, ..., b |
b, ..., a |
||
| 97 | SWAP8 | 3 | a, ..., b |
b, ..., a |
||
| 98 | SWAP9 | 3 | a, ..., b |
b, ..., a |
||
| 99 | SWAP10 | 3 | a, ..., b |
b, ..., a |
||
| 9A | SWAP11 | 3 | a, ..., b |
b, ..., a |
||
| 9B | SWAP12 | 3 | a, ..., b |
b, ..., a |
||
| 9C | SWAP13 | 3 | a, ..., b |
b, ..., a |
||
| 9D | SWAP14 | 3 | a, ..., b |
b, ..., a |
||
| 9E | SWAP15 | 3 | a, ..., b |
b, ..., a |
||
| 9F | SWAP16 | 3 | a, ..., b |
b, ..., a |
||
| A0 | LOG0 | A8(opens in a new tab) | ost, len |
. |
LOG0(memory[ost:ost+len-1]) | |
| A1 | LOG1 | A8(opens in a new tab) | ost, len, topic0 |
. |
LOG1(memory[ost:ost+len-1], topic0) | |
| A2 | LOG2 | A8(opens in a new tab) | ost, len, topic0, topic1 |
. |
LOG2(memory[ost:ost+len-1], topic0, topic1) | |
| A3 | LOG3 | A8(opens in a new tab) | ost, len, topic0, topic1, topic2 |
. |
LOG3(memory[ost:ost+len-1], topic0, topic1, topic2) | |
| A4 | LOG4 | A8(opens in a new tab) | ost, len, topic0, topic1, topic2, topic3 |
. |
LOG4(memory[ost:ost+len-1], topic0, topic1, topic2, topic3) | |
| A5-EF | invalid | |||||
| F0 | CREATE | A9(opens in a new tab) | val, ost, len |
addr |
addr = keccak256(rlp([address(this), this.nonce])) | |
| F1 | CALL | AA(opens in a new tab) | gas, addr, val, argOst, argLen, retOst, retLen |
success |
mem[retOst:retOst+retLen-1] := returndata | |
| F2 | CALLCODE | AA(opens in a new tab) | gas, addr, val, argOst, argLen, retOst, retLen |
success |
mem[retOst:retOst+retLen-1] = returndata | same as DELEGATECALL, but does not propagate original msg.sender and msg.value |
| F3 | 返回 | 0*(opens in a new tab) | ost, len |
. |
return mem[ost:ost+len-1] | |
| F4 | DELEGATECALL | AA(opens in a new tab) | gas, addr, argOst, argLen, retOst, retLen |
success |
mem[retOst:retOst+retLen-1] := returndata | |
| F5 | CREATE2 | A9(opens in a new tab) | val, ost, len, salt |
addr |
addr = keccak256(0xff ++ address(this) ++ salt ++ keccak256(mem[ost:ost+len-1]))[12:] | |
| F6-F9 | invalid | |||||
| FA | STATICCALL | AA(opens in a new tab) | gas, addr, argOst, argLen, retOst, retLen |
success |
mem[retOst:retOst+retLen-1] := returndata | |
| FB-FC | invalid | |||||
| FD | 撤销 | 0*(opens in a new tab) | ost, len |
. |
revert(mem[ost:ost+len-1]) | |
| FE | INVALID | AF(opens in a new tab) | designated invalid opcode - EIP-141(opens in a new tab) | |||
| FF | SELFDESTRUCT | AB(opens in a new tab) | addr |
. |
将所有以太币发送到 addr;如果在创建合约的同一交易中执行,它会销毁该合约 |





