【区块链安全 | 第二十四篇】单位和全局可用变量(二)
文章目录
- 单位和全局可用变量(Units and Globally Available Variables)
- 特殊变量和函数
- 1. 区块和交易属性
- 2. ABI 编码和解码函数
- 3. bytes 成员函数
- 4. string 成员函数
- 5. 错误处理
- 6. 数学和加密函数
- 7. 地址类型成员函数
- 8. 与合约相关
- 9. 类型信息

单位和全局可用变量(Units and Globally Available Variables)
特殊变量和函数
1. 区块和交易属性
在全局命名空间中始终存在一些特殊变量和函数,主要用于提供区块链相关信息或作为通用工具函数。
- blockhash(uint blockNumber) returns (bytes32):返回指定区块的哈希值,仅适用于最近 256 个区块;否则返回 0。
- blobhash(uint index) returns (bytes32):返回当前交易中第 index 个 blob 的版本化哈希值。版本化哈希由 1 个字节的版本号(目前为 0x01)和 KZG 承诺的 SHA256 哈希的后 31 个字节组成(EIP-4844)。如果不存在该索引的 blob,则返回 0。
- block.basefee (uint):当前区块的基础费用(EIP-3198 和 EIP-1559)。
- block.blobbasefee (uint):当前区块的 blob 基础费用(EIP-7516 和 EIP-4844)。
- block.chainid (uint):当前链的 Chain ID。
- block.coinbase (address payable):当前区块矿工的地址。
- block.difficulty (uint):当前区块的难度(仅适用于 Paris 之前的 EVM 版本)。在其他 EVM 版本中,它是 block.prevrandao 的废弃别名(EIP-4399)。
- block.gaslimit (uint):当前区块的 gas 限制。
- block.number (uint):当前区块的编号。
- block.prevrandao (uint):由信标链提供的随机数(适用于 EVM >= Paris)。
- block.timestamp (uint):当前区块的时间戳(自 Unix 纪元以来的秒数)。
- gasleft() returns (uint256):返回当前交易中剩余的 gas。
- msg.data (bytes calldata):完整的 calldata(调用数据)。
- msg.sender (address):消息(当前调用)的发送者地址。
- msg.sig (bytes4):calldata 的前 4 个字节(即函数标识符)。
- msg.value (uint):随消息发送的 Wei 数量。
- tx.gasprice (uint):交易的 gas 价格。
- tx.origin (address):交易的原始发送者地址(完整调用链中的最初发起者)。
注意
1.msg 变量的动态性
msg.sender、msg.value 等 msg 成员的值会在每次外部函数调用时发生变化,包括调用库函数时。
2.区块和交易属性的限制
当合约在链下执行(如本地测试或模拟环境)时,不要假设 block. 和 tx. 具有特定的值,这些值取决于 EVM 实现。
3.不要依赖 block.timestamp 或 blockhash 作为随机数源
block.timestamp 和 blockhash 可能受到矿工的操纵,在某些应用中,恶意矿工可以重复计算直到获得有利的哈希值。当前区块的时间戳必须严格大于上一个区块的时间戳,但唯一的保证是它位于两个连续区块的时间戳之间。
4.blockhash 的可用性
由于可扩展性原因,并非所有区块的哈希值都可用,只有最近 256 个区块的哈希值可访问,超过该范围的值将返回 0。
5.废弃的函数和别名:
block.blockhash 在 Solidity 0.4.22 版本被弃用,并在 0.5.0 版本中移除。
msg.gas 在 Solidity 0.4.21 版本被弃用,并在 0.5.0 版本中移除(替换为 gasleft())。
now(block.timestamp 的别名)在 Solidity 0.7.0 版本中被移除。
2. ABI 编码和解码函数
-
abi.decode(bytes memory encodedData, (…)) returns (…):对给定的 encodedData 进行 ABI 解码,第二个参数括号内指定解码后的数据类型。示例:
solidity
(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes)); -
abi.encode(…) returns (bytes memory):对给定的参数进行 ABI 编码。
-
abi.encodePacked(…) returns (bytes memory):对给定的参数进行紧凑编码(packed encoding)。注意:紧凑编码可能会导致数据歧义!
-
abi.encodeWithSelector(bytes4 selector, …) returns (bytes memory):以 selector 作为前缀,对后续参数进行 ABI 编码。
-
abi.encodeWithSignature(string memory signature, …) returns (bytes memory):等效于:
solidity
abi.encodeWithSelector(bytes4(keccak256(bytes(signature))), …) -
abi.encodeCall(function functionPointer, (…)) returns (bytes memory):对函数指针 functionPointer 及其参数进行 ABI 编码,同时进行完整的类型检查,确保参数类型与函数签名匹配。结果等价于:
solidity
abi.encodeWithSelector(functionPointer.selector, (…))
注意
1.这些编码函数可用于构造外部函数调用的数据,而不实际调用该函数。
2.keccak256(abi.encodePacked(a, b)) 可用于计算结构化数据的哈希值。警告:不同的参数类型可能导致哈希碰撞,应谨慎使用。
3.详细的 ABI 编码规则和紧凑编码(tightly packed encoding)请参考 Solidity 文档。
3. bytes 成员函数
bytes.concat(…) returns (bytes memory):将多个 bytes 和 bytes1 到 bytes32 类型的参数连接成一个字节数组。
4. string 成员函数
string.concat(…) returns (string memory):将多个字符串参数连接成一个字符串。
5. 错误处理
-
assert(bool condition):如果条件不成立,会触发 Panic 错误并回滚状态更改 - 用于内部错误。
-
require(bool condition):如果条件不成立,回滚并撤销交易 - 用于输入错误或外部组件错误。
-
require(bool condition, string memory message):如果条件不成立,回滚并撤销交易,并提供错误消息 - 用于输入错误或外部组件错误。
-
revert():中止执行并回滚状态更改。
-
revert(string memory reason):中止执行并回滚状态更改,并提供一个解释字符串。
6. 数学和加密函数
-
addmod(uint x, uint y, uint k) returns (uint) :计算 (x + y) % k,其中加法是以任意精度执行的,不会在 2^256 上溢出。从版本 0.5.0 起,确保 k != 0。
-
mulmod(uint x, uint y, uint k) returns (uint):计算 (x y) % k,其中乘法是以任意精度执行的,不会在 2^256 上溢出。从版本 0.5.0 起,确保 k != 0。
-
keccak256(bytes memory) returns (bytes32):计算输入的 Keccak-256 哈希值。
之前 keccak256 有一个别名叫 sha3,在版本 0.5.0 中已被移除。 -
sha256(bytes memory) returns (bytes32):计算输入的 SHA-256 哈希值。
-
ripemd160(bytes memory) returns (bytes20):计算输入的 RIPEMD-160 哈希值。
-
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address):从椭圆曲线签名中恢复与公钥相关联的地址,如果发生错误则返回零。该函数参数对应于 ECDSA 签名的值:
- r = 签名的前 32 字节
- s = 签名的第二个 32 字节
- v = 签名的最后 1 字节
ecrecover 返回一个地址,而不是一个可支付地址。如果需要将资金转移到恢复的地址,可以通过 address payable 进行转换。
进一步说明
在使用 ecrecover 时需要注意,一个有效的签名可以被转换成另一个有效的签名,而无需了解对应的私钥。在 Homestead 硬分叉中,针对交易签名的问题(参见 EIP-2)已被修复,但 ecrecover 函数未做更改。通常这不会造成问题,除非你要求签名是唯一的,或者使用签名来识别项目。你可以使用 OpenZeppelin 的 ECDSA 辅助库,它是 ecrecover 的封装,避免了这个问题。
注意
在私有区块链上运行 sha256、ripemd160 或 ecrecover 时,可能会遇到 “Out-of-Gas” 错误。这是因为这些函数作为“预编译合约”实现,只有在接收到第一个消息后才真正存在(尽管它们的合约代码是硬编码的)。发送到不存在的合约的消息更昂贵,因此执行可能会出现 “Out-of-Gas” 错误。解决该问题的方法是,先向每个合约发送 Wei(例如 1),然后再在实际合约中使用它们。这个问题在主网或测试网上并不存在。
7. 地址类型成员函数
1.<address>.balance (uint256) 返回地址的余额,以 Wei 为单位。
2.<address>.code (bytes memory) 返回地址处的代码(可能为空)。
3.<address>.codehash (bytes32) 返回地址的代码哈希。
4.<address payable>.transfer(uint256 amount) 向地址发送指定的 Wei 数量,失败时回滚,转发 2300 gas 补助,不可调节。
5.<address payable>.send(uint256 amount) returns (bool) 向地址发送指定的 Wei 数量,失败时返回 false,转发 2300 gas 补助,不可调节。
6.<address>.call(bytes memory) returns (bool, bytes memory) 进行低级 CALL,带有给定的有效载荷,返回成功状态和返回数据,转发所有可用的 gas,可以调节。
7.<address>.delegatecall(bytes memory) returns (bool, bytes memory) 进行低级 DELEGATECALL,带有给定的有效载荷,返回成功状态和返回数据,转发所有可用的 gas,可以调节。
8.<address>.staticcall(bytes memory) returns (bool, bytes memory) 进行低级 STATICCALL,带有给定的有效载荷,返回成功状态和返回数据,转发所有可用的 gas,可以调节。
警告
1.尽量避免使用 .call(),因为它绕过了类型检查、函数存在性检查和参数打包。
2.使用 send 时有一些危险:当调用栈深度达到 1024 时,转账会失败(这可以被调用者强制),如果接收者没有足够的 gas,也会失败。因此,为了确保安全的以太币转账,始终检查 send 的返回值,使用 transfer 或更好的方式:使用一种模式,让接收者主动提取以太币。
3.由于 EVM 将对不存在的合约的调用视为始终成功,因此 Solidity 在执行外部调用时使用 extcodesize 操作码进行了额外检查。这确保了即将调用的合约实际上存在(它包含代码),否则会抛出异常。对地址而不是合约实例进行操作的低级调用不包括此检查(即.call()、.delegatecall()、.staticcall()、.send() 和 .transfer()),这使得它们在 gas 上更便宜,但也更不安全。
注意
1.在版本 0.5.0 之前,Solidity 允许通过合约实例访问地址成员,例如 this.balance。现在已被禁止,必须显式转换为地址:address(this).balance。
2.如果通过低级 delegatecall 访问状态变量,两个合约的存储布局必须一致,以便被调用合约能够正3.确按名称访问调用合约的存储变量。当然,如果传递存储指针作为函数参数(如高层库的情况),则存储布局不会一致。
4.在版本 0.5.0 之前,.call、.delegatecall 和 .staticcall 只返回成功状态,而不返回返回数据。
5.在版本 0.5.0 之前,有一个名为 callcode 的成员,它与 delegatecall 的语义略有不同。
8. 与合约相关
1.this (当前合约的类型):当前合约,可以显式转换为地址类型。
2.super:继承层次结构中上一级的合约。
3.selfdestruct(address payable recipient): 销毁当前合约,将其资金发送到给定的地址并结束执行。selfdestruct有一些继承自 EVM 的特性:
- 接收合约的 receive 函数不会被执行。
- 合约仅在交易结束时被真正销毁,并且回滚可能会“撤销”销毁操作。
- 此外,当前合约的所有函数都可以直接调用,包括当前函数。
警告
1.从 EVM >= Cancun 开始,selfdestruct 将只会将账户中的所有以太币发送到给定的接收者,而不再销毁合约。然而,当 selfdestruct 在同一交易中被调用,并且创建了调用它的合约时,selfdestruct 会遵循 Cancun 硬分叉之前(即 EVM <= Shanghai)的行为,仍然会销毁当前合约,删除所有数据,包括存储键、代码和合约本身。详情请参见 EIP-6780。
2.新的行为是全网范围的变化,影响所有部署在以太坊主网和测试网的合约。需要注意的是,这一变化取决于合约部署链的 EVM 版本,编译合约时使用的 --evm-version 设置不会影响此行为。
3.此外,selfdestruct 操作码在 Solidity 版本 0.8.18 中已被弃用,按照 EIP-6049 的建议,弃用仍然有效,编译器会在使用时发出警告。在新部署的合约中强烈不建议使用,即使考虑到新的行为,未来 EVM 的更改可能会进一步减少该操作码的功能。
注意
在0.5.0版本之前,有一个名为suicide的函数,语义与selfdestruct相同。
9. 类型信息
表达式 type(X) 可用于检索关于类型 X 的信息。目前,支持此功能的类型有限(X 可以是合约类型或整数类型),但未来可能会扩展。
对于合约类型 C,以下属性可用:
-
type©.name:合约的名称
-
type©.creationCode:包含合约创建字节码的内存字节数组。可以在内联汇编中使用此字节码构建自定义创建例程,特别是通过使用 create2 操作码。该属性无法在合约本身或任何派生合约中访问,它会导致字节码被包含在调用站点的字节码中,因此像这样的循环引用是不可行的。
-
type©.runtimeCode:包含合约运行时字节码的内存字节数组。通常,这是合约 C 的构造函数部署的代码。如果 C 的构造函数使用了内联汇编,这可能与实际部署的字节码不同。还要注意,库在部署时会修改其运行时字节码,以防止常规调用。与 .creationCode 相同的限制也适用于此属性。
除了上述属性,以下属性适用于接口类型 I:
- type(I).interfaceId:一个 bytes4 值,包含给定接口 I 的 EIP-165 接口标识符。此标识符是接口中所有函数选择器的 XOR,排除了所有继承的函数。
对于整数类型 T,以下属性可用:
-
type(T).min:类型 T 可表示的最小值。
-
type(T).max:类型 T 可表示的最大值。
相关文章:
【区块链安全 | 第二十四篇】单位和全局可用变量(二)
文章目录 单位和全局可用变量(Units and Globally Available Variables)特殊变量和函数1. 区块和交易属性2. ABI 编码和解码函数3. bytes 成员函数4. string 成员函数5. 错误处理6. 数学和加密函数7. 地址类型成员函数8. 与合约相关9. 类型信息 单位和全…...
C语言:指针数组、函数、二级指针
1.指针数组 指针数组是一个数组,数组中的每个元素都是指针。这些指针可以指向各种类型的数据,如整数、字符、结构体等,甚至可以指向其他数组或函数。 指针数组的声明格式通常为: 数据类型 *数组名[数组大小];其中,数…...
批量修改记事本文本文件编码,可以解决文本文件乱码问题
对于文本文件来说,通常都可以设置不同的编码格式,每一种不同的编码格式支持的字符都可能是不一样的。因此当编码格式出现错误的时候,文本文件可能会出现乱码的问题。如何将文本文件的编码由一种格式变为另外一种格式呢?如果文件出…...
亚马逊云科技提供完全托管的DeepSeek-R1模型
近日,亚马逊云科技宣布在Amazon Bedrock上线完全托管的DeepSeek-R1模型。DeepSeek是首个登陆Amazon Bedrock的国产大模型,自今年1月底推出以来,已有数千客户使用Amazon Bedrock的自定义模型导入功能部署了DeepSeek-R1模型。 DeepSeek在过去几…...
Kafka简要介绍与快速入门示例
1、什么是Kafka? Kafka 是一个分布式的基于发布/订阅模式的消息队列(Message Queue),主要应用于大数据实时处理领域。 Kafka是一个分布式消息队列。Kafka对消息保存时根据Topic进行归类,发送消息者称为Producer&…...
线程池自顶向下
在一些场景下,线程会被频繁创建和销毁,但他们却始终在完成相似的任务 这个场景下我们回去引入一个线程池的概念 可以简单总结为: 任务提交 → 核心线程执行 → 任务队列缓存 → 非核心线程执行 → 拒绝策略处理。 话不多说先看一个简单的…...
利用 Chrome devTools Source Override 实现JS逆向破解案例
之前讲解 Chrome 一大强势技术 override 时,给的案例貌似没有给大家留下多深的印象 浏览器本地替换(local overrides)快速定位前端样式问题的案例详解(也是hook js的手段)_浏览器的 overrides 替换功能-CSDN博客 其实…...
Springboot 中使用 List<Integer> 与 JSONArray 处理 JSON 数组的性能与实践
深入对比:Springboot 中使用 List 与 JSONArray 处理 JSON 数组的性能与实践 引言 在现代 Web 开发中,处理 JSON 格式的数据是常见需求。当面对 POST 请求中的 JSON 数组时,开发者常需在 List<Integer> 和 JSONArray 两种方案间抉择。…...
容器C++ ——STL常用容器
string容器 string构造函数 #include<iostream> using namespace std; #include<string.h> void test01() {string s1;//默认构造const char* str "hello world";string s2(str);//传入char*cout << "s2" << s2 << endl;s…...
npu踩坑记录
之前使用qwen系列模型在ascend 910a卡进行了一些生成任务, 贴出踩坑过程也许对遇到类似问题的同学有帮助: ) 目录 千问 qwq32环境配置 代码部署 生成内容清洗 已生成内容清洗 生成过程优化 Failed to initialize the HCCP process问题 assistant 的历史回答丢失 推理执…...
Linux信号——信号的产生(1)
注:信号vs信号量:两者没有任何关系! 信号是什么? Linux系统提供的,让用户(进程)给其他进程发送异步信息的一种方式。 进程看待信号的方式: 1.信号在没有发生的时候,进…...
【机器学习】——机器学习思考总结
摘要 这篇文章深入探讨了机器学习中的数据相关问题,重点分析了神经网络(DNN)的学习机制,包括层级特征提取、非线性激活函数、反向传播和梯度下降等关键机制。同时,文章还讨论了数据集大小的标准、机器学习训练数据量的…...
html处理Base文件流
处理步骤 从服务返回的字符串中提取文件流数据,可能是Base64或二进制。将数据转换为Blob对象。创建对象URL。创建<a>元素,设置href和download属性。触发点击事件以下载文件。删除缓存数据 代码 // 假设这是从服务返回的Base64字符串(…...
力扣每日一题:2712——使所有字符相等的最小成本
使所有字符相等的最小成本 题目示例示例1示例2 题解这些话乍一看可能看不懂,但是多读两遍就明白了。很神奇的解法,像魔术一样。 题目 给你一个下标从 0 开始、长度为 n 的二进制字符串 s ,你可以对其执行两种操作: 选中一个下标…...
在MFC中使用Qt(六):深入了解QMfcApp
前言 此前系列文章回顾: 在MFC中使用Qt(一):玩腻了MFC,试试在MFC中使用Qt!(手动配置编译Qt) 在MFC中使用Qt(二):实现Qt文件的自动编译流程 在M…...
JMeter进行分布式压测
从机: 1、确认防火墙是否关闭; 2、打开网络设置,关闭多余端口;(避免远程访问不到) 3、打开JMeter/bin 目录底下的jmeter.properties; remove_hosts设置当前访问地址,192.XXXXX&…...
Python实现音频数字水印方法
数字水印技术可以将隐藏信息嵌入到音频文件中而不明显影响音频质量。下面我将介绍几种在Python中实现音频数字水印的方法。 方法一:LSB (最低有效位) 水印 import numpy as np from scipy.io import wavfile def embed_watermark_lsb(audio_path, watermark, ou…...
快速入手-基于Django-rest-framework的第三方认证插件(SimpleJWT)权限认证扩展返回用户等其他信息(十一)
1、修改serializer.py,增加自定义类 # 自定义用户登录token等返回信息 class MyTokenObtainPair(TokenObtainPairView): def post(self, request, *args, **kwargs): serializer self.get_serializer(datarequest.data) try: serializer.is_valid(raise_exceptio…...
关于IP免实名的那些事
IP技术已成为个人与企业保护隐私、提升网络效率的重要工具。其核心原理是通过中介服务器转发用户请求,隐藏真实IP地址,从而实现匿名访问、突破地域限制等目标。而“免实名”代理IP的出现,进一步简化了使用流程,用户无需提交身份信…...
【SQL性能优化】预编译SQL:从注入防御到性能飞跃
🔥 开篇:直面SQL的"阿喀琉斯之踵" 假设你正在开发电商系统🛒,当用户搜索商品时: -- 普通SQL拼接(危险!) String sql "SELECT * FROM products WHERE name "…...
Spring容器从启动到关闭的注解使用顺序及说明
Spring容器从启动到关闭的注解使用顺序及说明 1. 容器启动阶段 注解:Configuration、ComponentScan 作用: Configuration:标记配置类,声明Spring应用上下文的配置源。ComponentScan:扫描指定包下的组件(B…...
UVM概念面试题100问
1-10:UVM概述 Q1: 什么是UVM? A1: UVM是Universal Verification Methodology的缩写,它是由Accellera标准化的一种用于IC验证的方法学。它提供了一个基类库(BCL),包含通用工具如组件层次结构、事务级模型(TLM)和配置数据库等,使用户能够创建结构化、可重用的验证环境。 Q2:…...
SQL Server从安装到入门一文掌握应用能力。
本篇文章主要讲解,SQL Server的安装教程及入门使用的基础知识,通过本篇文章你可以快速掌握SQL Server的建库、建表、增加、查询、删除、修改等基本数据库操作能力。 作者:任聪聪 日期:2025年3月31日 一、SQL Server 介绍: SQL Server 是微软旗下的一款主流且优质的数据库…...
力扣HOT100之矩阵:54. 螺旋矩阵
这道题之前在代码随想录里刷过类似的,还有印象,我就按照当初代码随想录的思路做了一下,结果怎么都做不对,因为按照代码随想录的边界条件设置,当行数和列数都为奇数时,最后一个元素无法被添加到数组中&#…...
5.1 WPF路由事件以及文本样式
一、路由事件 WPF中存在一种路由事件(routed event),该事件将发送到包含该控件所在层次的所有控件,如果不希望继续向更高的方向传递,只要设置e.Handled true即可。 这种从本控件-->父控件->父的父控件的事件&am…...
Python数据可视化-第1章-数据可视化与matplotlib
环境 开发工具 VSCode库的版本 numpy1.26.4 matplotlib3.10.1 ipympl0.9.7教材 本书为《Python数据可视化》一书的配套内容,本章为第1章 数据可视化与matplotlib 本文主要介绍了什么是数据集可视化,数据可视化的目的,常见的数据可视化方式…...
Flutter敏感词过滤实战:基于AC自动机的高效解决方案
Flutter敏感词过滤实战:基于AC自动机的高效解决方案 在社交、直播、论坛等UGC场景中,敏感词过滤是保障平台安全的关键防线。本文将深入解析基于AC自动机的Flutter敏感词过滤实现方案,通过原理剖析实战代码性能对比,带你打造毫秒级…...
20250331-vue-组件事件1触发与监听事件
触发与监听事件 1 在组件的模板表达式中,可以直接使用 $emit 方法触发自定义事件(例如:在 v-on 的处理函数中): 子组件代码 <template><button click"$emit(someEvent)">点击</button> </template><…...
Odoo/OpenERP 和 psql 命令行的快速参考总结
Odoo/OpenERP 和 psql 命令行的快速参考总结 psql 命令行选项 选项意义-a从脚本中响应所有输入-A取消表数据输出的对齐模式-c <查询>仅运行一个简单的查询,然后退出-d <数据库名>指定连接的数据库名(默认为当前登录用户名)-e回显…...
Vue中使用antd-table组件时,树形表格展开配置不生效-defaultExpandedRowKeys-默认展开配置不生效
defaultExpandedRowKeys属性 defaultExpandAllRows这个属性仅仅是用来设置默认值的,只在第一次渲染的时候起作用,后续再去改变,无法实现响应式 解决方案一 a-table表格添加key属性,当每次获取值时,动态改变key,以达到重新渲染的效果 <a-table:key="tableKey"…...
