solidity官方文档对immutable介绍如下:
介绍中有两句话很有意思:
The contract creation code generated by the compiler will modify the contract’s runtime code
This is important if you are comparing the runtime code generated by the compiler with the one actually stored in the blockchain.
也就是说合约的 contract creation code会修改 contract runtime code,从而导致编译器生成的runtime code和实际执行构造函数后返回的runtime code 不同
Debug Immutable
测试合约如下
contract ImmutableValue{
uint public immutable value ;
constructor(){
value = 0xaaaaaaaa;
}
}
编译器生成的InitCode:
括号内部分代表构造函数的执行
(60a060405234801561001057600080fd5b5063aaaaaaaa6080818152505060805160d161003560003960006049015260d16000f3)fe6080604052348015600f57600080fd5b506004361060285760003560e01c80633fa4f24514602d575b600080fd5b60336047565b604051603e91906078565b60405180910390f35b7f000000000000000000000000000000000000000000000000000000000000000081565b6072816091565b82525050565b6000602082019050608b6000830184606b565b92915050565b600081905091905056fea2646970667358221220fb74a8a645b6bac20b4e35e1fe0faab46f46d68cd7a92c51f1c3af9b7e627a8364736f6c63430008000033
(PUSH1 0xA0 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH4 0xAAAAAAAA PUSH1 0x80 DUP2 DUP2 MSTORE POP POP PUSH1 0x80 MLOAD PUSH1 0xD1 PUSH2 0x35 PUSH1 0x0 CODECOPY PUSH1 0x0 PUSH1 0x49 ADD MSTORE PUSH1 0xD1 PUSH1 0x0 RETURN) INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH1 0x28 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x3FA4F245 EQ PUSH1 0x2D JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x33 PUSH1 0x47 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x3E SWAP2 SWAP1 PUSH1 0x78 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH32 0x0 DUP2 JUMP JUMPDEST PUSH1 0x72 DUP2 PUSH1 0x91 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 ADD SWAP1 POP PUSH1 0x8B PUSH1 0x0 DUP4 ADD DUP5 PUSH1 0x6B JUMP JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 SWAP1 POP SWAP2 SWAP1 POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 0xFB PUSH21 0xA8A645B6BAC20B4E35E1FE0FAAB46F46D68CD7A92C MLOAD CALL 0xC3 0xAF SWAP12 PUSH31 0x627A8364736F6C634300080000330000000000000000000000000000000000
我们把编译器的runtimeCode单独列出来如下:
6080604052348015600f57600080fd5b506004361060285760003560e01c80633fa4f24514602d575b600080fd5b60336047565b604051603e91906078565b60405180910390f35b7f000000000000000000000000000000000000000000000000000000000000000081565b6072816091565b82525050565b6000602082019050608b6000830184606b565b92915050565b600081905091905056fea2646970667358221220fb74a8a645b6bac20b4e35e1fe0faab46f46d68cd7a92c51f1c3af9b7e627a8364736f6c63430008000033
PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH1 0x28 JUMPI PUSH1 0x0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x3FA4F245 EQ PUSH1 0x2D JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x33 PUSH1 0x47 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x3E SWAP2 SWAP1 PUSH1 0x78 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH32 0x0 DUP2 JUMP JUMPDEST PUSH1 0x72 DUP2 PUSH1 0x91 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 ADD SWAP1 POP PUSH1 0x8B PUSH1 0x0 DUP4 ADD DUP5 PUSH1 0x6B JUMP JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 SWAP1 POP SWAP2 SWAP1 POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 0xFB PUSH21 0xA8A645B6BAC20B4E35E1FE0FAAB46F46D68CD7A92C MLOAD CALL 0xC3 0xAF SWAP12 PUSH31 0x627A8364736F6C634300080000330000000000000000000000000000000000
观察initcode 我们可以发现并没有任何一个sstore操作码,也就证明声明immutable的变量不会存储到storage空间的。
实际部署该合约:
先把构造函数的操作码拿过来:
PUSH1 0xA0 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH4 0xAAAAAAAA PUSH1 0x80 DUP2 DUP2 MSTORE POP POP
PUSH1 0x80 MLOAD PUSH1 0xD1 PUSH2 0x35 PUSH1 0x0 CODECOPY PUSH1 0x0 PUSH1 0x49 ADD MSTORE PUSH1 0xD1 PUSH1 0x0 RETURN
改变runtime code的过程主要从第二行的操作码开始:
执行PUSH1 0x80 MLOAD之后
执行PUSH1 0xD1 PUSH2 0x35 PUSH1 0x0之后(这个过程是为了codeCopy 做准备)
执行codecopy之后
执行PUSH1 0x0 PUSH1 0x49 ADD MSTORE
在执行之前我们观察下memory,可以发现在(0x49,0x69)这个mmeory都被0占据,刚好32字节的0
所以接下来要执行的PUSH1 0x0 PUSH1 0x49 ADD MSTORE这几个操作码就会将stack中的
0x00000000000000000000000000000000000000000000000000000000aaaaaaaa放到mem(0x49,0x69)这个位置,并作为runtimecode一部分返回出来
执行后如下:
最后返回的runtimecode如下:
6080604052348015600f57600080fd5b506004361060285760003560e01c80633fa4f24514602d575b600080fd5b60336047565b604051603e91906078565b60405180910390f35b7f00000000000000000000000000000000000000000000000000000000aaaaaaaa81565b6072816091565b82525050565b6000602082019050608b6000830184606b565b92915050565b600081905091905056fea2646970667358221220fb74a8a645b6bac20b4e35e1fe0faab46f46d68cd7a92c51f1c3af9b7e627a8364736f6c63430008000033
和编译器生成的runtime code不同
6080604052348015600f57600080fd5b506004361060285760003560e01c80633fa4f24514602d575b600080fd5b60336047565b604051603e91906078565b60405180910390f35b7f000000000000000000000000000000000000000000000000000000000000000081565b6072816091565b82525050565b6000602082019050608b6000830184606b565b92915050565b600081905091905056fea2646970667358221220fb74a8a645b6bac20b4e35e1fe0faab46f46d68cd7a92c51f1c3af9b7e627a8364736f6c63430008000033
结论:
实际执行构造函数得到的runtime code和编译器生成的的runtime code不同,因为00000000000000000000000000000000000000000000000000000000aaaaaaaa会在执行构造函数时替代掉到编译器生成的runtime code中提前为该immutable value预留的32字节0值