理解 EVM 解构Solidity合约 1 - 字节码
专栏 理解 EVM - 探究Solidity 背后的秘密 继续加入OpenZepplin 解构合约系列文章。
你在路上,开着你那辆罕见的、完全修复的1969年野马马赫1快速行驶。阳光照射在所有原始的、华丽的镀金轮辋上,闪闪发光。只有你,道路,沙漠,以及对地平线的无尽追逐。完美无缺!
眨眼间,你的335马力的野兽被白烟吞没,仿佛变成了一个蒸汽机车,你被迫停在路边。带着决心,你打开引擎盖,才意识到你完全不知道自己在看什么。这台该死的机器是如何工作的?你抓住你的手机,发现你没有信号。
如果这听起来很熟悉,那么不用担心!本系列文章的目的是为了让你了解你的合约。我们将解构一个简单的 Solidity 合约,查看它的字节码,并将其分解为可识别的结构,直至最低层。我们将揭开 Solidity 的盖子。在本系列结束时,你在查看或调试EVM字节码时应该感到熟悉。这个系列的全部意义在于揭开由 Solidity 编译器产生的 EVM 字节码的神秘面纱。而且它真的比看起来要简单得多。
注意:这个系列的目的是针对那些已经熟悉 Solidity 合约开发有经验的开发者,但想了解事情是如何在一个稍深/较底层的工作 - 即 Solidity 编译器如何将 Solidity 翻译成 EVM 的字节码,以及 EVM 如何执行这种字节码。如果你还没到那一步,我推荐你阅读Solidity 合约开发专栏
下面是我们要解构的合约:
pragma solidity ^0.4.24;
contract BasicToken {
uint256 totalSupply_;
mapping(address => uint256) balances;
constructor(uint256 _initialSupply) public {
totalSupply_ = _initialSupply;
balances[msg.sender] = _initialSupply;
}
function totalSupply() public view returns (uint256) {
return totalSupply_;
}
function transfer(address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[msg.sender]);
balances[msg.sender] = balances[msg.sender] - _value;
balances[_to] = balances[_to] + _value;
return true;
}
function balanceOf(address _owner) public view returns (uint256) {
return balances[_owner];
}
}
注意:这个合约很容易受到溢出攻击的影响,但我们在这里只是为了手头的工作而保持简单。
为了编译合约,我们将使用Remix。点击文件浏览区上方左上角的+按钮,创建一个新的合约。将文件名设为BasicToken.sol。现在,将上述代码粘贴到编辑器部分。
在左侧编译 Tab部分,进入编译设置,确保选择了启用优化。另外,确认所选的Solidity编译器的版本是: "0.4.24+commit.e67f0147"。这两个细节非常重要,否则你将会看到与这里讨论的略有不同的字节码。
如果你进入Compile标签并点击Bytecode按钮,会复制Solidity编译器生成的所有东西,其中之一是一个名为BYTECODE的JSON对象,它有一个 "object "属性,是合约的编译代码。它看起来像这样:
608060405234801561001057600080fd5b5060405160208061021783398101604090815290516000818155338152600160205291909120556101d1806100466000396000f3006080604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166318160ddd811461005b57806370a0823114610082578063a9059cbb146100b0575b600080fd5b34801561006757600080fd5b506100706100f5565b60408051918252519081900360200190f35b34801561008e57600080fd5b5061007073ffffffffffffffffffffffffffffffffffffffff600435166100fb565b3480156100bc57600080fd5b506100e173ffffffffffffffffffffffffffffffffffffffff60043516602435610123565b604080519115158252519081900360200190f35b60005490565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604090205490565b600073ffffffffffffffffffffffffffffffffffffffff8316151561014757600080fd5b3360009081526001602052604090205482111561016357600080fd5b503360009081526001602081905260408083208054859003905573ffffffffffffffffffffffffffffffffffffffff85168352909120805483019055929150505600a165627a7a72305820a5d999f4459642872a29be93a490575d345e40fc91a7cccb2cf29c88bcdaf3be0029
是的,这是完全不可读的(至少对一个正常人来说)。
接下来,进入Remix中的DEPLOY & RUN部分。在顶部,确保你使用的是Remix VM。这基本上是一个嵌入式的Javascript EVM+ 网络,是我们理想的以太坊游乐场。确保BasicToken被选中,并在Deploy输入框中输入数字10000。接下来,点击部署按钮。这将部署一个BasicToken合约的实例,初始发行量为10000个代币,由顶部当前选择的账户拥有,它将持有我们的代币发行总量。
在部署的合约部分,你应该看到已部署的合约,并有字段与它的三个功能交互:transfer、balanceOf 和 totalSupply。在这里,我们将能够与我们刚刚部署的合约的实例进行交互。
但在此之前,让我们看看 "部署 "合约到底意味着什么。在控制台区域,你应该看到日志 "create of BasicToken pending...",然后是一个带有各种字段的交易条目:from、to、value、data、logs和 hash。点击这个条目来扩展交易的信息。即使是缩写,你也应该看到该交易的 input 数据是我们上面介绍的相同的字节码(加上参数)。这个交易被发送到0x0地址,结果是,一个新的合约实例被创建,有它自己的地址和代码。我们将在下一篇文章中详细研究这个过程.
在交易数据的右边,仍然在控制台中,点击Debug(调试)按钮。这将激活Remix右侧区域的调试器标签。让我们来看看指令部分。如果你向下滚动,你应该看到以下内容。
000 PUSH1 80
002 PUSH1 40
004 MSTORE
005 CALLVALUE
006 DUP1
007 ISZERO
008 PUSH2 0010
011 JUMPI
012 PUSH1 00
014 DUP1
015 REVERT
016 JUMPDEST
017 POP
018 PUSH1 40
020 MLOAD
021 PUSH1 20
023 DUP1
024 PUSH2 0217
027 DUP4
028 CODECOPY
029 DUP2
030 ADD
031 PUSH1 40
033 SWAP1
034 DUP2
035 MSTORE
036 SWAP1
037 MLOAD
038 PUSH1 00
040 DUP2
041 DUP2
042 SSTORE
043 CALLER
044 DUP2
045 MSTORE
046 PUSH1 01
048 PUSH1 20
050 MSTORE
051 SWAP2
052 SWAP1
053 SWAP2
054 SHA3
055 SSTORE
056 PUSH2 01d1
059 DUP1
060 PUSH2 0046
063 PUSH1 00
065 CODECOPY
066 PUSH1 00
068 RETURN
069 STOP
070 PUSH1 80
072 PUSH1 40
074 MSTORE
075 PUSH1 04
077 CALLDATASIZE
078 LT
079 PUSH2 0056
082 JUMPI
083 PUSH4 ffffffff
088 PUSH29 0100000000000000000000000000000000000000000000000000000000
118 PUSH1 00
120 CALLDATALOAD
121 DIV
122 AND
123 PUSH4 18160ddd
128 DUP2
129 EQ
130 PUSH2 005b
133 JUMPI
134 DUP1
135 PUSH4 70a08231
140 EQ
141 PUSH2 0082
144 JUMPI
145 DUP1
146 PUSH4 a9059cbb
151 EQ
152 PUSH2 00b0
155 JUMPI
156 JUMPDEST
157 PUSH1 00
159 DUP1
160 REVERT
161 JUMPDEST
162 CALLVALUE
163 DUP1
164 ISZERO
165 PUSH2 0067
168 JUMPI
169 PUSH1 00
171 DUP1
172 REVERT
173 JUMPDEST
174 POP
175 PUSH2 0070
178 PUSH2 00f5
181 JUMP
182 JUMPDEST
183 PUSH1 40
185 DUP1
186 MLOAD
187 SWAP2
188 DUP3
189 MSTORE
190 MLOAD
191 SWAP1
192 DUP2
193 SWAP1
194 SUB
195 PUSH1 20
197 ADD
198 SWAP1
199 RETURN
200 JUMPDEST
201 CALLVALUE
202 DUP1
203 ISZERO
204 PUSH2 008e
207 JUMPI
208 PUSH1 00
210 DUP1
211 REVERT
212 JUMPDEST
213 POP
214 PUSH2 0070
217 PUSH20 ffffffffffffffffffffffffffffffffffffffff
238 PUSH1 04
240 CALLDATALOAD
241 AND
242 PUSH2 00fb
245 JUMP
246 JUMPDEST
247 CALLVALUE
248 DUP1
249 ISZERO
250 PUSH2 00bc
253 JUMPI
254 PUSH1 00
256 DUP1
257 REVERT
258 JUMPDEST
259 POP
260 PUSH2 00e1
263 PUSH20 ffffffffffffffffffffffffffffffffffffffff
284 PUSH1 04
286 CALLDATALOAD
287 AND
288 PUSH1 24
290 CALLDATALOAD
291 PUSH2 0123
294 JUMP
295 JUMPDEST
296 PUSH1 40
298 DUP1
299 MLOAD
300 SWAP2
301 ISZERO
302 ISZERO
303 DUP3
304 MSTORE
305 MLOAD
306 SWAP1
307 DUP2
308 SWAP1
309 SUB
310 PUSH1 20
312 ADD
313 SWAP1
314 RETURN
315 JUMPDEST
316 PUSH1 00
318 SLOAD
319 SWAP1
320 JUMP
321 JUMPDEST
322 PUSH20 ffffffffffffffffffffffffffffffffffffffff
343 AND
344 PUSH1 00
346 SWAP1
347 DUP2
348 MSTORE
349 PUSH1 01
351 PUSH1 20
353 MSTORE
354 PUSH1 40
356 SWAP1
357 SHA3
358 SLOAD
359 SWAP1
360 JUMP
361 JUMPDEST
362 PUSH1 00
364 PUSH20 ffffffffffffffffffffffffffffffffffffffff
385 DUP4
386 AND
387 ISZERO
388 ISZERO
389 PUSH2 0147
392 JUMPI
393 PUSH1 00
395 DUP1
396 REVERT
397 JUMPDEST
398 CALLER
399 PUSH1 00
401 SWAP1
402 DUP2
403 MSTORE
404 PUSH1 01
406 PUSH1 20
408 MSTORE
409 PUSH1 40
411 SWAP1
412 SHA3
413 SLOAD
414 DUP3
415 GT
416 ISZERO
417 PUSH2 0163
420 JUMPI
421 PUSH1 00
423 DUP1
424 REVERT
425 JUMPDEST
426 POP
427 CALLER
428 PUSH1 00
430 SWAP1
431 DUP2
432 MSTORE
433 PUSH1 01
435 PUSH1 20
437 DUP2
438 SWAP1
439 MSTORE
440 PUSH1 40
442 DUP1
443 DUP4
444 SHA3
445 DUP1
446 SLOAD
447 DUP6
448 SWAP1
449 SUB
450 SWAP1
451 SSTORE
452 PUSH20 ffffffffffffffffffffffffffffffffffffffff
473 DUP6
474 AND
475 DUP4
476 MSTORE
477 SWAP1
478 SWAP2
479 SHA3
480 DUP1
481 SLOAD
482 DUP4
483 ADD
484 SWAP1
485 SSTORE
486 SWAP3
487 SWAP2
488 POP
489 POP
490 JUMP
491 STOP
492 LOG1
493 PUSH6 627a7a723058
500 SHA3
501 INVALID
502 INVALID
503 SWAP10
504 DELEGATECALL
505 GASLIMIT
506 SWAP7
507 TIMESTAMP
508 DUP8
509 INVALID
510 INVALID
511 INVALID
512 SWAP4
513 LOG4
514 SWAP1
515 JUMPI
516 INVALID
517 CALLVALUE
518 INVALID
519 BLOCKHASH
520 INVALID
521 SWAP2
522 INVALID
523 INVALID
524 INVALID
525 INVALID
526 CALLCODE
527 SWAP13
528 DUP9
529 INVALID
530 INVALID
531 RETURN
532 INVALID
533 STOP
534 INVALID
535 STOP
536 STOP
537 STOP
538 STOP
539 STOP
540 STOP
541 STOP
542 STOP
543 STOP
544 STOP
545 STO...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!