【区块链安全 | 第十四篇】类型之值类型(一)

文章目录

  • 值类型
    • 布尔值
    • 整数
      • 运算符
      • 取模运算
      • 指数运算
    • 定点数
    • 地址(Address)
      • 类型转换
      • 地址成员
        • balance 和 transfer
        • send
        • call,delegatecall 和 staticcall
        • code 和 codehash
    • 合约类型(Contract Types)
    • 固定大小字节数组(Fixed-size byte arrays)
    • 地址字面量(Address Literals)

在这里插入图片描述

Solidity 是一种静态类型语言,这意味着每个变量(无论是状态变量还是局部变量)都需要指定其类型。Solidity 提供了几种基本类型,这些类型可以组合形成复杂类型。

此外,不同类型可以在包含运算符的表达式中相互作用,并且具有优先级的区分。

Solidity 没有“未定义”或“空”值的概念,但新声明的变量总是具有默认值,该默认值取决于其类型。为了处理任何意外的值,应该使用 revert 函数回滚整个交易,或者返回一个带有布尔值的元组,其中第二个 bool 值表示操作是否成功。

值类型

值类型的变量始终按值传递,即在作为函数参数或赋值时总是被复制。

与引用类型不同,值类型的声明不指定数据位置,因为它们足够小,可以存储在栈中。唯一的例外是状态变量,状态变量默认存储在存储中,但也可以标记为 transient、constant 或 immutable。

布尔值

bool:值是 true 或 false。

整数

int / uint:各种大小的有符号和无符号整数。

关键字 uint8 到 uint256(以 8 为步长,表示 8 位到 256 位的无符号整数),以及 int8 到 int256。

uint 和 int 分别是 uint256 和 int256 的别名。

运算符

比较运算符:<=,<,==,!=,>=,>(结果为 bool)

位运算符:&,|,^(按位异或),~(按位取反)

移位运算符:<<(左移),>>(右移)

算术运算符:+,-,一元 -(仅适用于有符号整数),*,/,%(取模),**(指数运算)

对于整数类型 X,可以使用 type(X).min 和 type(X).max 来访问该类型可表示的最小值和最大值。

运算符 || 和 && 遵循短路规则。这意味着在表达式 f(x) || g(y) 中,如果 f(x) 计算结果为 true,则 g(y) 将不会被计算。

注意
Solidity 中的整数受到特定范围的限制。例如,对于 uint32,其范围为 0 到 2**32 - 1。

在 Solidity 中,整数运算有两种模式:“溢出”模式(wrapping/unchecked mode) 和 “检查”模式(checked mode)。

默认情况下,运算始终处于 “检查”模式,这意味着如果运算结果超出了类型的值范围,则调用会因失败的断言而回滚。

可以使用 unchecked { ... } 切换到 “溢出”模式,但应谨慎使用。

取模运算

% 的结果是操作数 a 除以操作数 n 后的余数 r,其中 q = int(a / n),并且 r = a - (n * q)。

这意味着取模运算的结果与其左操作数的符号相同(或为零),并且对于负数 a,a % n == -(-a % n) 恒成立:

int256(5) % int256(2) == int256(1)int256(5) % int256(-2) == int256(1)int256(-5) % int256(2) == int256(-1)int256(-5) % int256(-2) == int256(-1)

注意:使用 0 作为取模运算的除数会导致 Panic 错误。此检查无法通过 unchecked { ... } 关闭。

指数运算

指数运算 ** 仅适用于无符号类型作为指数(幂)。指数运算的结果类型始终与底数的类型相同。请确保底数足够大以容纳结果,并预防潜在的断言失败或溢出行为。

注意:在 检查模式(checked mode)下,对于较小的底数,指数运算仅使用相对廉价的 EXP 操作码。

例如,在计算 x**3 时,使用 x*x*x 可能更便宜。因此,建议进行 Gas 成本测试 并使用优化器。此外,EVM 规定 0**0 的结果为 1。

定点数

警告:定点数在 Solidity 中尚不完全支持。它们可以被声明,但不能进行赋值操作。

fixed / ufixed:带符号和无符号定点数,具有不同的大小。关键字 ufixedMxN 和 fixedMxN,其中 M 代表类型所占用的位数,N 代表可用的小数位数。M 必须是 8 的倍数,范围从 8 到 256 位。N 必须在 0 到 80 之间(包含 0 和 80)。ufixed 和 fixed 是 ufixed128x18 和 fixed128x18 的别名。

操作符

  • 比较操作符:<=,<,==,!=,>=,>(结果为布尔值)

  • 算术操作符:+,-,一元 -(仅对带符号数),*,/,%(取模)

注意:浮动点数(许多语言中的 float 和 double,更精确地说是 IEEE 754 数字)和定点数的主要区别在于,浮动点数用于表示整数和小数部分的位数是灵活的,而定点数则严格定义了每部分所占的位数。通常,在浮动点数中,几乎整个空间用于表示数字,而只有少数位数用于定义小数点的位置。

地址(Address)

地址类型有两种主要相似的变体:

  • address:持有一个 20 字节的值(以太坊地址的大小)。
  • address payable:与 address 相同,但具有额外的 transfer 和 send 成员。

这种区分的想法是,address payable 是一个可以接收以太币的地址,而普通的 address 不能接收以太币,例如,它可能是一个不支持接收以太币的智能合约。

类型转换

1.允许从 address payable 到 address 的隐式转换,而从 address 到 address payable 必须通过显式转换 payable(<address>)。
2.允许显式转换到 address 类型并返回 uint160、整数字面量、bytes20 和合约类型。
3.只有 address 类型和合约类型的表达式可以通过显式转换 payable(...) 转换为 address payable。对于合约类型,只有在合约可以接收以太币的情况下(即合约有 receive 或 payable 回退函数)才能进行这种转换。注意,payable(0) 是有效的,且是这一规则的例外。

注意
如果我们需要一个 address 类型的变量,并计划向其发送以太币,那么应将其声明为 address payable 以使该要求更加明显。此外,尽量在最早阶段做出这种区分或转换。

从 0.5.0 版本开始,address 和 address payable 之间的区分被引入。自那时起,合约不能隐式转换为 address 类型,但如果它们有 receive 或 payable 回退函数,仍然可以显式地转换为 address 或 address payable。

地址成员

balance 和 transfer

可以使用 balance 属性查询地址的余额,并使用 transfer 函数将以太币(以 wei 为单位)发送到可支付的地址:

address payable x = payable(0x123);
address myAddress = address(this);
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);

transfer 函数在当前合约的余额不足或接收方帐户拒绝接收以太币时失败,并会回滚操作。

注意
如果 x 是一个合约地址,它的代码(更具体地说:其 Receive Ether 函数如果存在,或其他回退函数如果存在)将在 transfer 调用时一起执行,这是 EVM 的特性,无法阻止。如果该执行耗尽了 gas 或以任何方式失败,Ether 转账将回滚,当前合约将停止并抛出异常。

send

send 是 transfer 的低级对等函数。如果执行失败,当前合约不会停止并抛出异常,而是返回 false。

使用 send 时存在一些危险:如果调用堆栈深度达到 1024(调用者可以强制此情况),或者接收方耗尽 gas,则转账将失败。因此,为了安全地转账以太币,始终检查 send 的返回值,使用 transfer 或更好的方式是使用一个模式,其中接收方自己提取以太币。

call,delegatecall 和 staticcall

为了与不符合 ABI 的合约交互,或为了更直接地控制编码,可以使用 call、delegatecall 和 staticcall 函数。它们都接受一个字节内存参数,并返回成功条件(布尔值)和返回的数据(字节内存)。abi.encode、abi.encodePacked、abi.encodeWithSelector 和 abi.encodeWithSignature 可以用来编码结构化数据。

示例:

bytes memory payload = abi.encodeWithSignature("register(string)", "MyName");
(bool success, bytes memory returnData) = address(nameReg).call(payload);
require(success);

注意,这些都是低级函数,应该小心使用。特别是,任何未知的合约可能是恶意的,如果调用它,你将把控制权交给该合约,而该合约可能会再次调用你的合约,因此当调用返回时,需要做好准备处理可能会更改的状态变量。与其他合约交互的常规方式是调用合约对象上的函数(例如 x.f())。

可以使用 gas 修饰符调整提供的 gas:

address(nameReg).call{gas: 1000000}(abi.encodeWithSignature("register(string)", "MyName"));

类似地,也可以控制提供的 Ether 数量:

address(nameReg).call{value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));

最后,这些修饰符可以结合使用,它们的顺序无关紧要:

address(nameReg).call{gas: 1000000, value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));

类似地,可以使用 delegatecall 函数,不同之处在于,只有给定地址的代码会被使用,所有其他方面(存储、余额等)都来自当前合约。delegatecall 的目的是使用存储在另一个合约中的库代码。用户必须确保两个合约的存储布局适合使用 delegatecall。

code 和 codehash

你可以查询任何智能合约的已部署代码。使用 .code 获取 EVM 字节码作为字节内存(可能为空)。使用 .codehash 获取该代码的 Keccak-256 哈希(作为 bytes32)。注意,addr.codehash 比使用 keccak256(addr.code) 更便宜。

如果与 addr 相关联的帐户为空或不存在(即它没有代码、零余额和零 nonce,如 EIP-161 所定义),则 addr.codehash 的输出可能为 0。如果该帐户没有代码,但有非零余额或 nonce,则 addr.codehash 将输出空数据的 Keccak-256 哈希(即 keccak256(""),其结果为 c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)。

注意,所有合约都可以转换为 address 类型,因此可以使用 address(this).balance 查询当前合约的余额。

合约类型(Contract Types)

每个合约都定义了自己的类型。你可以将合约隐式转换为它继承的合约类型。合约类型可以显式地转换为 address 类型,反之亦然。

显式转换到 address payable 类型仅在合约类型有 receive 或 payable 回退函数时才可能。转换仍然通过 address(x) 进行。如果合约类型没有 receive 或 payable 回退函数,可以使用 payable(address(x)) 进行转换。

注意
1.如果你声明一个合约类型的局部变量(例如 MyContract c),你可以在该合约上调用函数。需要注意的是,必须从与之相同的合约类型赋值给该变量。
2.你也可以实例化合约(这意味着它们是新创建的)。你可以在“通过 new 创建合约”部分找到更多的细节。
3.合约的数据显示方式与 address 类型相同,并且这种类型也用于 ABI 中。
4.合约不支持任何操作符。
5.合约类型的成员是该合约的外部函数,包括任何标记为 public 的状态变量。
6.对于合约 C,你可以使用 type(C) 来访问有关该合约的类型信息。

固定大小字节数组(Fixed-size byte arrays)

值类型 bytes1, bytes2, bytes3, …, bytes32 用于存储从 1 到 32 字节的字节序列。

操作符:

  • 比较操作符:<=, <, ==, !=, >=, >(返回布尔值)
  • 位操作符:&, |, ^(按位异或),~(按位取反)
  • 移位操作符:<<(左移),>>(右移)
  • 索引访问:如果 x 是类型 bytesI,则 x[k](0 <= k < I)返回第 k 个字节(只读)。

移位操作符与无符号整数类型作为右操作数一起工作(但返回左操作数的类型),表示要移位的位数。使用有符号类型进行移位会导致编译错误。

成员.length 可以返回字节数组的固定长度(只读)。

注意
类型 bytes1[] 是字节的数组,但由于填充规则,对于每个元素,它浪费 31 字节的空间(在存储中除外)。最好使用 bytes 类型。

地址字面量(Address Literals)

地址字面量是通过地址校验和测试的十六进制字面量,例如 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF 是 address 类型。长度在 39 到 41 位之间且未通过校验和测试的十六进制字面量会产生错误。

我们可以通过在前面(对于整数类型)或后面(对于 bytesNN 类型)加零来去除错误。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/bicheng/75286.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows 系统下多功能免费 PDF 编辑工具详解

IceCream PDF Editor是一款极为实用且操作简便的PDF文件编辑工具&#xff0c;它完美适配Windows操作系统。其用户界面设计得十分直观&#xff0c;哪怕是初次接触的用户也能快速上手。更为重要的是&#xff0c;该软件具备丰富多样的强大功能&#xff0c;能全方位满足各类PDF编辑…

vue3相比于vue2的提升

性能提升&#xff1a; Vue3的页面渲染速度更快、性能更好。特别是在处理大量数据和复杂组件时&#xff0c;优势更加明显。Vue3引入了编译时优化&#xff0c;如静态节点提升&#xff08;hoistStatic&#xff09;、补丁标志&#xff08;patchflag&#xff09;等&#xff0c;这些…

Redis 梳理汇总目录

Redis 哨兵集群&#xff08;Sentinel&#xff09;与 Cluster 集群对比-CSDN博客 如何快速将大规模数据保存到Redis集群-CSDN博客 Redis的一些高级指令-CSDN博客 Redis 篇-CSDN博客

【奇点时刻】GPT-4o新生图特性深度洞察报告

以下报告围绕最新推出的「GPT4o」最新图像生成技术展开&#xff0c;旨在让读者从整体层面快速了解其技术原理、功能亮点&#xff0c;以及与其他常见图像生成或AI工具的对比分析&#xff0c;同时也会客观探讨该技术在应用过程中可能遇到的挑战与限制。 1. 技术背景概述 GPT4o新…

【算法day28】解数独——编写一个程序,通过填充空格来解决数独问题

37. 解数独 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图&#xff…

【已解决】Javascript setMonth跨月问题;2025-03-31 setMonth后变成 2025-05-01

文章目录 bug重现解决方法&#xff1a;用第三方插件来实现&#xff08;不推荐原生代码来实现&#xff09;。项目中用的有dayjs。若要自己实现&#xff0c;参考 AI给出方案&#xff1a; bug重现 今天&#xff08;2025-04-01&#xff09;遇到的一个问题。原代码逻辑大概是这样的…

力扣刷题-热题100题-第29题(c++、python)

19. 删除链表的倒数第 N 个结点 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/?envTypestudy-plan-v2&envIdtop-100-liked 计算链表长度 对于链表&#xff0c;难的就是不知道有多少元素&#xff…

【QT】QT的多界面跳转以及界面之间传递参数

QT的多界面跳转以及界面之间传递参数 一、在QT工程中添加新的界面二、多界面跳转的两种情况1、A界面跳到B界面&#xff0c;不需要返回2、A界面跳到B界面&#xff0c;需要返回1&#xff09;使用this指针传递将当前界面地址传递给下一界面2&#xff09;使用parentWidget函数获取上…

【力扣hot100题】(022)反转链表

非常经典&#xff0c;我写的比较复杂&#xff0c;一直以来的思路都是这样&#xff0c;就没有去找更简单的解法&#xff1a;&#xff08;做链表题习惯加头结点的前置节点了&#xff0c;去掉也行&#xff09; /*** Definition for singly-linked list.* struct ListNode {* …

剑指Offer(数据结构与算法面试题精讲)C++版——day2

剑指Offer(数据结构与算法面试题精讲)C++版——day2 题目一:只出现一次的数据题目二:单词长度的最大乘积题目三:排序数组中的两个数字之和题目一:只出现一次的数据 一种很简单的思路是,使用数组存储出现过的元素,比如如果0出现过,那么arr[0]=1,但是有个问题,题目中没…

【C++游戏引擎开发】《线性代数》(3):矩阵乘法的SIMD优化与转置加速

一、矩阵乘法数学原理与性能瓶颈 1.1 数学原理 矩阵乘法定义为:给定两个矩阵 A ( m n ) \mathrm{A}(mn) A(mn)和 B ( n p ) \mathrm{B}(np) B(np),它们的乘积 C = A B \mathrm{C}=AB C=AB 是一个 m p \mathrm{m}p mp 的矩阵,其中: C i , j = ∑ k = 1…

Vue Transition组件类名+TailwindCSS

#本文教学结合TailwindCSS实现一个Transition动画的例子# 举例代码&#xff1a; <transition enter-active-class"transition-all duration-300 ease-out"enter-from-class"opacity-0 translate-y-[-10px]"enter-to-class"opacity-100 translate-…

技术回顾day2

1.获取文件列表 流程&#xff1a;前端根据查询条件封装查询信息&#xff0c;后端接收后进行封装&#xff0c;封装为FileInfoQuery,根据fileInfoQuery使用mybatis的动态sql来进行查询。 2.文件分片上传 每次上传需要上传包括(文件名字&#xff0c;文件&#xff0c;md5值&#…

DeepSeek-R1 模型现已在亚马逊云科技上提供

2025年3月10日更新—DeepSeek-R1现已作为完全托管的无服务器模型在Amazon Bedrock上提供。 2025年2月5日更新—DeepSeek-R1 Distill Llama 和 Qwen模型现已在Amazon Bedrock Marketplace和Amazon SageMaker JumpStart中提供。 在最近的Amazon re:Invent大会上&#xff0c;亚马…

STP --- 生成树协议

协议信息 配置 BPDU Protocol identifier&#xff1a;协议标识 Version&#xff1a;协议版本&#xff1a;STP 为 0&#xff0c;RSTP 为 2&#xff0c;MSTP 为 3 type&#xff1a; BPDU 类型 Flag&#xff1a; 标志位 Root ID&#xff1a; 根桥 ID&#xff0c;由两字节的优…

Ansible playbook-ansible剧本

一.playbook介绍 便于功能的重复使用 本质上就是文本文件&#xff0c;一般都是以.yml结尾的文本文件。 1.遵循YAML语法 1.要求同级别代码要有相同缩进&#xff0c;建议4个空格。【同级别代码是同一逻辑的代码】 在计算机看来空格和Tob键是两个不同的字符。 2.一个键对应一…

python的基础入门

初识Python 什么是Python Python是1门程序设计语言。在开发者眼里&#xff0c;语言可以分为3类&#xff1a; 自然语言&#xff1a;人能听懂的语言&#xff0c;例如汉语&#xff0c;英语&#xff0c;法语等等。机器语言&#xff1a;机器能听懂的语言&#xff0c;机器只能听懂0…

MD编辑器中的段落缩进怎么操作

在 Markdown&#xff08;MD&#xff09;编辑器中&#xff0c;段落的缩进通常可以通过 HTML 空格符、Markdown 列表缩进、代码块缩进等方式 实现。以下是几种常见的段落缩进方法&#xff1a; 1. 使用全角空格 ( ) 在一些 Markdown 编辑器&#xff08;如 Typora&#xff09;中&…

8.neo4j图数据库python操作

使用图数据库的原因 图数据库使用neo4j的原因&#xff1a;neo4j使用率高&#xff0c;模板好找&#xff0c;报错能查。 红楼梦人物关系图地址 GraphNavigator neo4j学习手册 https://www.w3cschool.cn/neo4j/neo4j_need_for_graph_databses.html CQL代表的是Cypher查询语言…

[Lc6_记忆化搜索] 扫雷游戏 | 理解 递归vs记忆化搜索vs dp

目录 ⭕1.扫雷游戏 题解 1.记忆化搜索 解法一&#xff1a;递归 解法二&#xff1a;记忆化搜索 解法三&#xff1a;动态规划 ⭕1.扫雷游戏 (暴力模拟&#xff09; 链接&#xff1a;529. 扫雷游戏 让我们一起来玩扫雷游戏&#xff01; 给你一个大小为 m x n 二维字符矩阵…