区块链知识系列 - 系统学习EVM(二)

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

特点

  • EVM出于所谓运算速度和效率方面考虑采用了非主流的256bit整数。
  • 不支持浮点数
  • 缺乏标准库支持,例如字符串拼接、切割、查找等等都需要开发者自己实现
  • 给合约打补丁或是部分升级合约代码在EVM中是完全不可能的

存储

image-20221125135742652

Code

code 部署合约时储存 data 字段也就是合约内容的空间即专门存储智能合约的二进制源码的空间

Storage

Storage 是一个可以读写修改的持久存储的空间也是每个合约持久化存储数据的地方。Storage 是一个巨大的 map一共 2 256 2^{256} 2256 个插槽 (slot)每个插糟有 32 bytes合约中的“状态变量”会根据其具体类型分别保存到这些插槽中。

Stack

stack 即所谓的“运行栈"用来保存 EVM 指令的输入和输出数据。可以免费使用没有 gas 消耗用来保存函数的局部变量数量被限制在 16 个。stack 的最大深度为 1024 其中每个单元是 32 byte。

Args

args 也叫 calldata是一段只读的可寻址的保存函数调用参数的空间与栈不同的地方的是如果要使用 calldata 里面的数据必须手动指定偏移量和读取的字节数。

Memory

Memory 一个简单的字节数组主要是在运行期间存储数据将参数传递给内部函数。基于 32 byte 进行寻址和扩展。

安全

溢出攻击

EVM 的 safeMath 库不是默认使用例如开发者对 solidity 的 uint256 做计算的时候如果最终结果大于 uint256 的最大值就会产生溢出变为一个很小的数这样就产生了溢出漏洞。诸如 BEC、SMT 等相关币种都遭受过溢出攻击带来了极度严重都后果
对策: 利用一些第三方的safemath库来保证这个数字操作的安全

重入攻击

solidity 一大特性是可以调用外部其他合约但在将 eth 发送给外部地址或者调用外部合约的时候 需要合约提交外部调用。如果外部地址是恶意合约攻击者可以在 Fallback 函数中加入恶意代码当发生转账的时候就会调用 Fallback 函数执行恶意代码恶意代码会执行调用合约的有漏洞函数导致转账重新提交。最严重的重入攻击发生在以太坊早期即知名的 DAO 漏洞 - (先转账再扣钱,结果转账到一个恶意合约地址)。
对策: 在金额转移之前一定要先扣钱

非预期函数执行

EVM 没有严格检查函数调用如果合约地址作为传入参数可控可能导致非预期行为发生。

权限问题,如果构造函数(0.4.20之前的版本)名和合约名写的不一致,就会被外部或者其他合约所调用,恶意的攻击者可能获取了我们当前智能合约的所有权
对策: 注意编码规范保证合约名与构造函数名相同。如果现在使用构造函数我们建议使用constractor来创建

时间戳依赖

时间戳就可以被矿工修改的

可预测的随机数

使用区块变量生成随机数,所有的区块变量都可以被矿工操纵.因为这些区块变量在同一区块上是共用的。攻击者通过其恶意合约调用受害者合约那么此交易打包在同一区块中其区块变量是一样的。

对策: 通过Oracle获取随机数

服务攻击

withdraw时,不要直接把币转到用户指定的某个地址(可能是个恶意合约), 而是让用户主动提取自己的押金

不要空投,而是让人主动来领取.

私有数据

不要以为声明为private的变量外部就无法访问到

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract DCA {
    uint counter;
    uint private age;

    constructor(uint _c) {
        counter = _c;
        age = _c + 2;
    }
}

通过api可直接按内存地址访问到private的数据

import web3

url = ""
w3 = web3.Web3(web3.HTTPProvider(url))

w3.eth.get_storage_at(address, 0x0)
#一个uint占256bit, 即32字节 #HexBytes('0x0000000000000000000000000000000000000000000000000000000000000005')
w3.eth.get_storage_at(address, 0x1)
# HexBytes('0x0000000000000000000000000000000000000000000000000000000000000007')

注意

tx.origin和msg.sender区别

  • msg.sender (address): 消息发送方 (当前调用)

  • tx.origin(address): 交易发送方完整调用链上的原始发送方

用户A -> 合约1 -> 合约2

若在合约2中使用 msg.sender会得到合约1的地址

若在合约2中使用 tx.origin会得到用户A即整个调用链的起点

合约间的调用方式call、delegatecall

调用方式修改storage调用者的msg.sender被调用者的msg.sender执行上下文件
call修改被调用者的合约storage交易的发起者地址调用者地址在被调用者里
delegatecall修改调用者的合约storage交易的发起者地址调用者地址在调用者里

receive 和 fallback 调用流程

接收以太功能函数

solidity 接收函数 receive 没有参数、没有返回值。

solidity 向合约转账发送 Eth就会执行 receive 函数。

如果没有定义接收函数 receive就会执行 fallback 函数。

fallback函数,即 回退函数,没有名字,没有参数,没有返回值

function(){
}

往期精彩回顾:
区块链知识系列
密码学系列
零知识证明系列
共识系列
公链调研系列
BTC系列
以太坊系列
EOS系列
Filecoin系列
联盟链系列
Fabric系列
智能合约系列
Token系列
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: 区块链