Ethereum-SE04

Solidity

Solc

一般的话我不直接使用,基本都是用truffle或者remix来管理,除非说自己编译之后看些东西

solc-select:solc多版本管理工具

https://docs.soliditylang.org/en/latest/using-the-compiler.html

修饰符

view vs pure

  1. view修饰非改变链上状态的函数
  2. pure修饰非改变链上状态也不读取链上状态的函数

总结:pure是view的更严格子集,对于直接web3调用来说,都是不消耗gas费的。

https://docs.soliditylang.org/en/latest/contracts.html#view-functions

public vs external

  1. public修饰的函数,可以外部调用,也可以合约本身直接调用(func(...))。
  2. external修饰的函数,只能外部调用,合约本身不能直接调用(但是能通过this.func(...)调用)。

总结建议:在合约内部如果非特殊情况,不要使用this.func(...)来调用自身合约函数(会耗费更多的gas费,因为是通过CALL(gas: 700)来调用,而非JUMP调用(gas: 8)。)

基本上能用public用public,external在一些特殊场合使用(比如让代码更严谨)

1
2
3
4
5
6
7
8
9
10
11
12
13
> import "@openzeppelin/contracts/access/Ownable.sol";
> contract A is Ownable {
> /// 在这里public修饰符就没有什么作用了,除非owner权限是合约本身
> function abc() external onlyOwner {
> _abc();
> }
> function _abc() internal {
> }
> function def() public {
> _abc();
> }
> }
>

https://docs.soliditylang.org/en/latest/cheatsheet.html?highlight=encodewithsignature#function-visibility-specifiers

estimateGas估算gas

以太坊开发和普通开发不同的地方就是以太坊的每一步操作都是耗费gas的,一个好的合约,应该考虑到gas的消耗。

gas消耗

以太坊黄皮中的Fee Schedule介绍了不同指令的gas消耗

总结:SLOAD、SSTORE是我们经常使用的耗费gas操作,即读取链上数据和写入数据到链上。

节省gas

  1. 当链上数据读出来之后需要多次用到并计算。先读出来存为单独变量(栈内存),之后再用该变量进行计算。最后再写入链上。

Ethereum Yellow Paper

动态调用

delegatecall

代理调用可以简单理解为将一个合约变成library之后进行调用,调用环境不变(调用者环境)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
contract B {
address public delegator;
uint public save = 50;
uint public abc = 10;

function set(uint256 amount) public {
save = amount;
}
}

contract A {
address public delegator;
uint public save;

function set(
address target,
uint amount
) public {
//solium-disable-next-line
(bool success, ) =
target.delegatecall(
abi.encodeWithSignature(
'set(uint256)',
amount
)
);
}
}
  1. B合约可以单独进行调用,也可以被A调用。
  2. B合约被代理调用时,所有的存储槽环境变成A的环境,即save的值不再是50,而是取决于A中save的值。在B的set被调用时,save值也会写到A的合约中,而B的save值不动。
  3. B合约被单独调用时,save变量为自己的存储值。
  4. abc在B中被定义,但是在A中未定义,也可以被修改,但是A合约中无法进行调用(但是如果相同槽位上有变量,则会修改对应存储槽的变量)

总结:B在被直接调用时,B合约存储值为自己的内容,当B被代理调用时,存储槽环境全部更换为代理者合约环境,期间所有的修改都不会影响B合约本身。

https://docs.soliditylang.org/en/latest/introduction-to-smart-contracts.html?highlight=delegatecall#delegatecall-callcode-and-libraries

状态变量存储

https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html

32 byte 为一个 solt(uint256, bytes32都各占一个solt)

address为20 byte address: Holds a 20 byte value (size of an Ethereum address)

constant修饰的变量不占用存储槽 constant for state variables: Disallows assignment (except initialisation), does not occupy storage slot.

new

当我们在一个合约中创建新的合约时,创建的新合约的opcode会在调用者合约中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pragma solidity ^0.8.6;

interface IERC20 {
}

contract ERC20 is IERC20 {
string name = "USD";
}

contract A {
ERC20 a;
constructor() public {
a = new ERC20();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
======= test.sol:A =======
EVM assembly:
/* "test.sol":101:186 contract A {... */
mstore(0x40, 0x80)
/* "test.sol":131:184 constructor() public {... */
........

sub_1: assembly {
/* "test.sol":46:99 contract ERC20 is IERC20 {... */
mstore(0x40, 0x80)
/* "test.sol":77:96 string name = "USD" */
mload(0x40)
........

======= test.sol:ERC20 =======
EVM assembly:
/* "test.sol":46:99 contract ERC20 is IERC20 {... */
mstore(0x40, 0x80)
/* "test.sol":77:96 string name = "USD" */
mload(0x40)
........


======= test.sol:A =======
Opcodes:
PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD PUSH2 0x1D SWAP1 PUSH2 0x7E JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 PUSH1 0x0 CREATE DUP1 ISZERO DUP1 ISZERO PUSH2 0x39 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP PUSH1 0x0 DUP1 PUSH2 0x100 EXP DUP2 SLOAD DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF MUL NOT AND SWAP1 DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND MUL OR SWAP1 SSTORE POP PUSH2 0x8B JUMP JUMPDEST PUSH2 0x1B3 DUP1 PUSH2 0xD8 DUP4 CODECOPY ADD SWAP1 JUMP JUMPDEST PUSH1 0x3F DUP1 PUSH2 0x99 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 0xDD SDIV MLOAD PUSH31 0x7B25D4872F547C10A31A984A2D7C1D483446A87AB62163602BE036B464736F PUSH13 0x63430008060033608060405260 BLOCKHASH MLOAD DUP1 PUSH1 0x40 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x3 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x5553440000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP PUSH1 0x0 SWAP1 DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 PUSH2 0x4F SWAP3 SWAP2 SWAP1 PUSH2 0x62 JUMP JUMPDEST POP CALLVALUE DUP1 ISZERO PUSH2 0x5C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x166 JUMP JUMPDEST DUP3 DUP1 SLOAD PUSH2 0x6E SWAP1 PUSH2 0x105 JUMP JUMPDEST SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 PUSH1 0x1F ADD PUSH1 0x20 SWAP1 DIV DUP2 ADD SWAP3 DUP3 PUSH2 0x90 JUMPI PUSH1 0x0 DUP6 SSTORE PUSH2 0xD7 JUMP JUMPDEST DUP3 PUSH1 0x1F LT PUSH2 0xA9 JUMPI DUP1 MLOAD PUSH1 0xFF NOT AND DUP4 DUP1 ADD OR DUP6 SSTORE PUSH2 0xD7 JUMP JUMPDEST DUP3 DUP1 ADD PUSH1 0x1 ADD DUP6 SSTORE DUP3 ISZERO PUSH2 0xD7 JUMPI SWAP2 DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH2 0xD6 JUMPI DUP3 MLOAD DUP3 SSTORE SWAP2 PUSH1 0x20 ADD SWAP2 SWAP1 PUSH1 0x1 ADD SWAP1 PUSH2 0xBB JUMP JUMPDEST JUMPDEST POP SWAP1 POP PUSH2 0xE4 SWAP2 SWAP1 PUSH2 0xE8 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST JUMPDEST DUP1 DUP3 GT ISZERO PUSH2 0x101 JUMPI PUSH1 0x0 DUP2 PUSH1 0x0 SWAP1 SSTORE POP PUSH1 0x1 ADD PUSH2 0xE9 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x2 DUP3 DIV SWAP1 POP PUSH1 0x1 DUP3 AND DUP1 PUSH2 0x11D JUMPI PUSH1 0x7F DUP3 AND SWAP2 POP JUMPDEST PUSH1 0x20 DUP3 LT DUP2 EQ ISZERO PUSH2 0x131 JUMPI PUSH2 0x130 PUSH2 0x137 JUMP JUMPDEST JUMPDEST POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH32 0x4E487B7100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 MSTORE PUSH1 0x22 PUSH1 0x4 MSTORE PUSH1 0x24 PUSH1 0x0 REVERT JUMPDEST PUSH1 0x3F DUP1 PUSH2 0x174 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 0xC0 0xE1 0xDF 0xD7 MSIZE 0xEA PUSH26 0x8FB3EFE0406836E938644954EE98B21122AF4D07FDA309D6E064 PUSH20 0x6F6C634300080600330000000000000000000000

======= test.sol:ERC20 =======
Opcodes:
PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x40 MLOAD DUP1 PUSH1 0x40 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x3 DUP2 MSTORE PUSH1 0x20 ADD PUSH32 0x5553440000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE POP PUSH1 0x0 SWAP1 DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 PUSH2 0x4F SWAP3 SWAP2 SWAP1 PUSH2 0x62 JUMP JUMPDEST POP CALLVALUE DUP1 ISZERO PUSH2 0x5C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x166 JUMP JUMPDEST DUP3 DUP1 SLOAD PUSH2 0x6E SWAP1 PUSH2 0x105 JUMP JUMPDEST SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 PUSH1 0x1F ADD PUSH1 0x20 SWAP1 DIV DUP2 ADD SWAP3 DUP3 PUSH2 0x90 JUMPI PUSH1 0x0 DUP6 SSTORE PUSH2 0xD7 JUMP JUMPDEST DUP3 PUSH1 0x1F LT PUSH2 0xA9 JUMPI DUP1 MLOAD PUSH1 0xFF NOT AND DUP4 DUP1 ADD OR DUP6 SSTORE PUSH2 0xD7 JUMP JUMPDEST DUP3 DUP1 ADD PUSH1 0x1 ADD DUP6 SSTORE DUP3 ISZERO PUSH2 0xD7 JUMPI SWAP2 DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH2 0xD6 JUMPI DUP3 MLOAD DUP3 SSTORE SWAP2 PUSH1 0x20 ADD SWAP2 SWAP1 PUSH1 0x1 ADD SWAP1 PUSH2 0xBB JUMP JUMPDEST JUMPDEST POP SWAP1 POP PUSH2 0xE4 SWAP2 SWAP1 PUSH2 0xE8 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST JUMPDEST DUP1 DUP3 GT ISZERO PUSH2 0x101 JUMPI PUSH1 0x0 DUP2 PUSH1 0x0 SWAP1 SSTORE POP PUSH1 0x1 ADD PUSH2 0xE9 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x2 DUP3 DIV SWAP1 POP PUSH1 0x1 DUP3 AND DUP1 PUSH2 0x11D JUMPI PUSH1 0x7F DUP3 AND SWAP2 POP JUMPDEST PUSH1 0x20 DUP3 LT DUP2 EQ ISZERO PUSH2 0x131 JUMPI PUSH2 0x130 PUSH2 0x137 JUMP JUMPDEST JUMPDEST POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH32 0x4E487B7100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 MSTORE PUSH1 0x22 PUSH1 0x4 MSTORE PUSH1 0x24 PUSH1 0x0 REVERT JUMPDEST PUSH1 0x3F DUP1 PUSH2 0x174 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 0xC0 0xE1 0xDF 0xD7 MSIZE 0xEA PUSH26 0x8FB3EFE0406836E938644954EE98B21122AF4D07FDA309D6E064 PUSH20 0x6F6C634300080600330000000000000000000000

合约的部署其实就是将opcode存储到链上

漏洞

闪电贷

delegatecall

调用攻击合约修改调用合约的存储信息

Reference

Ethereum Yellow Paper