Solidity 中的数学(第 2 部分:溢出)

这篇具有很好参考价值的文章主要介绍了Solidity 中的数学(第 2 部分:溢出)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文是关于在 Solidity 中进行数学运算的系列文章中的第二篇。这次的主题是:溢出。

Solidity 中的数学(第 2 部分:溢出)

介绍

每次我看到+***审计另一个 Solidity 智能合约时,我都会开始写以下评论:“这里可能会溢出”。我需要几秒钟来写这四个字,在这几秒钟内,我观察附近的行,试图找出原因,为什么不可能溢出,或者为什么在这种特殊情况下应该允许溢出。如果找到原因,我会删除评论,但大多数情况下评论会保留在最终审计报告中。

事情不应该是这样的。算术运算符应该允许编写紧凑且易于阅读的公式,例如a**2 + 2*a*b + b**2. 然而,这个表达式几乎肯定会引起一堆安全问题,真正的代码更可能是这样的:

add (add (pow (a, 2), mul (mul (2, a), b)), pow (b, 2))

这里addmulpow分别是实现 、 和 的“安全”版本+*函数**

不鼓励简洁方便的语法,很少使用简单的算术运算符(一次不超过一个),到处都是繁琐且不可读的函数语法。在这篇文章中,我们分析了这个问题,它让事情变得如此奇怪,它臭名昭著的名字是:溢出

我们在某处走错了路

有人会说,溢出总是存在的,所有的编程语言都深受其害。但这真的是真的吗?您是否见过类似为 C++、Python 或 JavaScript 实现的SafeMath库?在证明相反的情况之前,您真的认为每个+*都是安全漏洞吗?很可能,您对这两个问题的回答都是“否”。所以,

为什么 Solidity 溢出会如此痛苦?

剧透:无处可逃,无处可藏。

数字在纯数学中不会溢出。可以将两个任意大数相加并得到精确的结果。在 JavaScript 和 Python 等高级编程语言中,数字不会溢出。在某些情况下,结果可能会落入无穷大,但至少将两个正数相加可能永远不会产生负结果。在 C++ 和 Java 中,整数会溢出,但浮点数不会。

在那些整数类型确实会溢出的语言中,纯整数主要用于索引、计数器和缓冲区大小,即用于受正在处理的数据大小限制的值。对于可能超出普通整数范围的值,有浮点数、大整数和大十进制数据类型,它们是内置的或通过库实现的。

基本上,当算术运算的结果不适合参数的类型时,编译器可能会做一些选择:i)使用更宽的结果类型;ii) 返回截断的结果并使用侧通道通知程序溢出;iii) 抛出异常;和 iv) 只是默默地返回截断的结果。

int在处理类型溢出时,第一个选项在 Python 2 中实现。第二个选项是 CPU 中的进位/溢出标志的用途。第三个选项由 SafeMath 库为 Solidity 实现。第四个选项是 Solidity 自己实现的。

第四个选项可能是最糟糕的一个,因为它使算术运算容易出错,同时使溢出检测非常昂贵,特别是对于乘法情况。为了安全起见,需要在每次乘法后执行额外的除法。

因此,Solidity 既没有安全类型,可以跑到,也没有安全操作,可以躲在后面。无处可逃,无处可躲,开发人员不得不面对溢出并在整个代码中与它们作斗争。

那么,下一个问题是:

为什么 Solidity 没有安全类型和操作?

剧透:因为 EVM 没有它们。

智能合约必须是安全的。它们中的错误和漏洞会造成数百万美元的损失,因为我们已经从惨痛的教训中吸取了教训。作为智能合约开发的主要语言,Solidity 非常重视安全性。如果有许多功能应该可以防止开发人员搬起石头砸自己的脚。我们指的是payable关键字、类型转换限制等功能。每个主要版本都会添加此类功能,通常会破坏向后兼容性,但社区为了更好的安全性而容忍这种情况。

然而,基本的算术运算非常不安全,现在几乎没有人直接使用它们,而且情况也没有改善。唯一变得更安全的操作是除法:除以零以前返回零,但现在它抛出异常,但即使是除法也没有变得完全安全,因为它仍然可能溢出。是的,在intSolidity 类型中,当 -2¹²⁷ 除以 -1 时会溢出,因为正确答案 (2¹²⁷) 不适合int. 所有其他操作,即+-***仍然容易发生溢出或下溢,因此本质上是不安全的。

Solidity 中的算术运算复制相应的 EVM 操作码的行为,并且使这些运算在编译器级别安全会增加数倍的气体消耗。普通ADD操作码需要 3 个 gas。作者设法找到的用于实现安全添加的最便宜的操作码序列是:

DUP2(3) DUP2(3) NOT(3) LT(3) <overflow>(3) JUMPI(10) ADD(3)

<overflow>是溢出时跳转的地址。括号中的数字是操作的 gas 成本,这些数字总共给我们 28 gas。几乎是 plain 的 10 倍ADD。太多了,对吧?这取决于你比较的是什么。比如说,从 SafeMath 库调用add函数将花费大约 88 gas。

因此,库或编译器级别的安全算术成本很高,但是

为什么 EVM 没有安全的算术操作码?

剧透:没有充分的理由。

出于性能原因,有人会说 EVM 中的算术语义复制了 CPU 的算术语义。是的,一些现代 CPU 有256 位运算的操作码,但主流 EVM 实现似乎不使用这些操作码。Geth 使用big.IntGo 编程语言标准库中的类型。这种类型实现了由原生单词数组支持的任意宽大整数。Parity 使用自己的库在本机 64 位字之上实现固定宽度的大整数。

对于这两种实现,算术溢出检测的额外成本几乎为零。因此,一旦 EVM 拥有算术操作码版本,在溢出时恢复,它们的 gas 成本可以与现有的不安全版本相同,或者略高。

更有用的是完全不溢出的操作码,而是返回整个结果。这样的操作码将允许在编译器或库级别有效地实现任意宽的大整数。

我们不知道为什么 EVM 没有上述操作码。也许只是因为其他主流虚拟机没有它们?

到目前为止,我们讲述的是真正的溢出:计算结果太大而不适合结果数据类型的情况。现在是时候发现问题的另一面了:

幻影溢出

如何计算 Solidity 中x的 3%?在主流语言中,人们只是写0.03*x,但 Solidity 不支持分数。怎么样x*3/100?好吧,这在大多数情况下都有效,但是如果x太大以至于x*3会溢出怎么办?从上一节我们知道该怎么做,对吧?只需使用mulSafeMath 并确保安全:mul (x, 3) / 100......没那么快。

后一个版本更安全一些,因为它会还原前一个版本返回错误结果的位置。这很好,但是……到底为什么计算 3% 的东西可能会溢出?某物的 3% 保证低于原始价值:无论是名义价值还是绝对价值。所以,只要x适合 256 位字,那么x的 3%也应该适合,不是吗?

好吧,我称之为“幻影溢出”:最终计算结果适合结果数据类型,但某些中间操作溢出的情况。

虚拟溢出比真实溢出更难检测和解决。一种解决方案是对中间值使用更宽的整数类型甚至浮点类型。另一个是重构表达式以使幻像溢出成为不可能。让我们尝试用我们的表达式来做后者。

算术定律告诉我们,以下公式应该产生相同的结果:

(x * 3) / 100
(3 * x) / 100
(x / 100) * 3
(3 / 100) * x

但是,Solidity 中的整数除法与纯数学中的除法不同,因为在 Solidity 中它将结果四舍五入为零。前两个变体基本上是等价的,并且都存在幻象溢出。第三种变体没有幻象溢出问题,但不太精确,尤其是对于小x。第四个变体更有趣,因为它会令人惊讶地导致编译错误:

browser/Junk.sol:5:18: TypeError: Operator * not compatible with types rational_const 3 / 10 and uint256
browser/Junk.sol:5:18: TypeError: Operator * not compatible with types rational_const 3 / 10 and uint256

我们已经在上一篇文章中中描述了这种行为。为了使第四个表达式编译,我们需要像这样更改它:

(uint (3) / 100) * x

然而,这并没有多大帮助,因为更正后的表达式的结果始终为零,因为3 / 100向零四舍五入就是零。

通过第三个变体,我们设法以精度为代价解决了幻影溢出问题。实际上,精度损失仅对小x显着,而对于大x则可以忽略不计。请记住,对于原始表达式,只有大x才会出现幻像溢出问题,因此我们似乎可以像这样组合这两种变体:

x > SOME_LARGE_NUMBER ? x / 100 * 3 : x * 3 / 100

这里SOME_LARGE_NUMBER可以计算为 (2²⁵⁶-1)/3 并将该值向下舍入。现在对于小x我们使用原始公式,而对于大x我们使用不允许幻像溢出的修改公式。看起来我们现在解决了幻像溢出问题,而没有显着降低精度。干得好,对吧?

在这种特殊情况下,可能是的。但是,如果我们需要计算的不是 3%,而是 3.1415926535% 怎么办?公式为:

x > SOME_LARGE_NUMBER ?
x / 1000000000000 * 31415926535 :
x * 31415926535 / 1000000000000

我们的SOME_LARGE_NUMBER意愿变为 (2²⁵⁶-1)/31415926535。那时没有那么大。那么 3.141592653589793238462643383279% 呢?这种方法适用于简单的情况,但似乎不能很好地扩展。

结论

EVM 不提供溢出保护操作码。Solidity 在编译器级别不提供任何溢出保护。因此,智能合约开发人员必须在代码级别解决溢出问题,这使得代码变得繁琐且 gas 效率较低。

幻象溢出更难检测和解决。直截了当会导致权衡取舍,而且通常无法扩展。

在我们的下一篇文章中,我们将提出解决幻影溢出问题的更好方法

 文章来源地址https://www.toymoban.com/news/detail-411293.html

到了这里,关于Solidity 中的数学(第 2 部分:溢出)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C语言的栈溢出问题以及部分解决】

    例如:针对学习过程中遇到的栈溢出问题 溢出,常见的解释是: 程序外部的数据大小,超出其规定数据类型所能表达的范围,从而造成正常程序预期外的错误表达。 溢出一般被当做黑客攻击操作系统的途径。具体的溢出类型有以下三种: 缓冲区溢出(Buffer overflow);内存溢

    2024年01月21日
    浏览(28)
  • CSS(ellipsis) 用百分比宽度将字符串溢出部分显示为省略号

    通常使用 ellipsis 将溢出部分显示为省略号必须给出指定像素宽度(width:200px),这样不方便共享响应式的样式定义,经过查资料,可以用 CSS 的 calc() 函数实现。

    2024年02月07日
    浏览(40)
  • Excel将单元格中的json本文格式化

    打开Excel文件并按下ALT + F11打开Visual Basic for Applications(VBA)编辑器。 输入下面的代码 进入https://github.com/VBA-tools/VBA-JSON,下载release的最新代码 VBA编辑器,“文件”-“导入”, 导入文件 JsonConverter.bas 点击VBA编辑器的菜单栏上的\\\"工具\\\"。 选择\\\"引用\\\",然后在弹出的对话框中找到

    2024年02月14日
    浏览(49)
  • 【Flink】关于jvm元空间溢出,mysql binlog冲突的问题解决

    Caused by: io.debezium.DebeziumException: A slave with the same server_uuid/server_id as this slave has connected to the master; the first event ‘’ at 4, the last event read from ‘/home/mysql/log/mysql/mysql-bin.003630’ at 62726118, the last byte read from ‘/home/mysql/log/mysql/mysql-bin.003630’ at 62726118. Error code: 1236; SQLSTATE: HY000. 网上

    2024年02月10日
    浏览(40)
  • Java中的内存泄露、内存溢出与栈溢出

    大家好,我是欧阳方超。本次就Java中几个相似而又不同的概念做一下介绍。内存泄漏、内存溢出和栈溢出都是与内存相关的问题,但它们之间有所不同。 我们经常会遇到内存泄漏、内存溢出和栈溢出等问题,这些问题都与内存的使用有关。 内存泄漏(memory leak)指的是程序

    2024年02月03日
    浏览(72)
  • 【区块链技术开发】 关于Windows10平台Solidity语言开发环境配置

    在 Windows 上配置 Solidity 语言开发环境需要进行以下步骤:

    2023年04月20日
    浏览(67)
  • 关于STM32的hal库中滴答定时器uwTick溢出的思考和分析

    思考:如果中断函数HAL_IncTick中的uwTick一直增加导致溢出会不会导致延时不准? 下面展示一些 STM32的官方库文件stm32f1xx_hal.c部分摘录 。 系统Tick频率设置为1毫秒中断一次,每1ms产生一次Tick中断, 在Tick中断中uwTick++; 那么当自增到0xFFFFFFFF后就会溢出,再从0开始自增 。 0xFFFF

    2024年03月21日
    浏览(45)
  • 离散数学——图论部分

    目录 概述考点: 邻接矩阵,矩阵的计算及含义,完全图,补图,平面图的相关概念,欧拉图,最小生成树,最优二叉树 一.图 ​编辑   二.路和回路 2.1 2.2连通与可达 1.可达 2.连通 三.图的矩阵表示 3.1邻接矩阵 3.2可达性矩阵 3.3无向图的完全关联矩阵 3.4有向图的完全关联矩阵

    2024年02月04日
    浏览(41)
  • 数学建模部分算法

    Q1:模拟退火是什么算法? 模拟退火是模拟物理上退火方法,通过N次迭代(退火),逼近函数的上的一个 最值 (最大或者最小值)。 比如逼近这个函数的最大值C点: Q2:模拟退火为什么可行? 讨论这个问题需要理解一下物理原型是怎么样的,也就是原来是怎么“退火”的:

    2024年02月08日
    浏览(33)
  • 彻底理解solidity中的事件

    在我之前的几篇关于智能合约的文章中,都有提到事件的用法,比如: 这里定义了两个事件,分别表示最高竞价更新了和拍卖结束了。 然后在需要的位置,调用事件,比如: 我们可以通过emit调用事件方法,然后这个事件就作为日志记录到了以太坊区块链中。日志是以太坊区

    2024年02月02日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包