以太坊是一个可编程的区块链,其功能可以通过将称为智能合约的可执行代码片段发布到区块链本身来扩展。这将以太坊与第一代区块链区分开来,在第一代区块链中,新功能需要修改客户端软件、升级节点以及分叉整个区块链。
智能合约是在链上发布的一段可执行代码,分配有唯一的区块链地址。智能合约控制属于其地址的所有资产,并在与其他智能合约交互时可以代表该地址进行操作。每个智能合约都有持久存储,用于保存调用之间的智能合约状态。
Solidity是以太坊以及其他几个使用以太坊虚拟机 (EVM) 的区块链平台上智能合约开发的主要编程语言。
编程总是关于数学的,区块链总是关于金融的,金融自古就是关于数学的(或者也许数学就是关于金融的)。作为以太坊区块链的主要编程语言,Solidity 必须做好数学运算。
在本系列中,我们讨论了 Solidity 如何进行数学运算的各个方面,以及开发人员如何在 Solidity 中进行数学运算。第一个要讨论的话题是:数字。
与主流编程语言相比,Solidity 的数值类型相当多:即 5,248。是的,根据文档,有 32 种有符号整数、32 种无符号整数、2592 种有符号定点和 2592 种无符号定点类型。JavaScript 只有两种数字类型。Python 2 曾经有四个,但在 Python 3 中删除了“long”类型,所以现在只有三个。Java 有七个,C++ 大约有十四个。
有这么多数字类型,Solidity 应该有适合每个人的类型,对吧?没那么快。让我们仔细看看这些数字类型。
我们将从以下问题开始:
剧透:我们没有。
纯数学中没有数字类型。一个数可能是整数或非整数,有理数或无理数,正数或负数,实数或虚数等,但这些只是属性,数字可能有也可能没有,单个数字可能同时具有多个这样的属性。
许多高级编程语言都有单一的数字类型。在 2019 年引入“BigInt”之前,JavaScript 只有“数字”。
除非做硬核低级的东西,否则开发人员并不真正需要多种数字类型,他们只需要具有任意范围和精度的纯数字。然而,这些数字本身不受硬件支持,并且在软件中模拟有些昂贵。
这就是为什么低级编程语言和以高性能为目标的语言通常具有多种数值类型,例如有符号/无符号,8/16/32/64/128位宽,整数/浮点数等。这些类型是原生支持的硬件,广泛用于文件格式、网络协议等,因此低级代码从中受益。
然而,出于性能原因,这些类型通常会继承底层 CPU 指令的所有奇怪语义,例如静默溢出和下溢、不对称范围、二进制分数、字节顺序问题等。这使得它们在高级业务逻辑代码中很痛苦。直接使用通常显得不安全,而安全使用通常变得麻烦且不可读。
那么,下一个问题是:
剧透:它没有。
EVM 原生支持两种数据类型:256 位字和 8 位字节。堆栈元素、存储键和值、指令和内存指针、时间戳、余额、交易和块哈希、地址等都是 256 位字。内存、字节码、调用数据、返回数据都是由字节组成的。大多数 EVM 操作码都处理单词,包括所有数学运算。一些数学运算将单词视为有符号整数,一些作为无符号整数,而其他运算只是以相同的方式工作,而不管参数是否在无符号上签名。
所以 EVM 原生支持两种数值类型:有符号 256 位整数和无符号 256 位整数。这些类型在 Solidity 中分别称为int和uint。
除了这两种类型(以及它们的别名int256和uint256)之外,Solidity 有 62 种整数类型int<N>,和uint<N>,其中<N>可以是 8 到 248 之间的任意 8 的倍数,即 8、16、…、248。在 EVM 级别上,所有这些类型都由相同的 256 位字,但每个操作的结果都被截断为 N 位。它们可能对特定情况有用,当需要特定位宽时,但对于一般计算,这些类型只是功能较弱且效率较低(在每个操作后截断不是免费的)版本int和uint.
最后,Solidity 有 5184 种定点类型fixedNxM,ufixedNxM其中 N 是从 8 到 256 的 8 的倍数,N 是从 0 到 80 的整数。这些类型应该实现各种范围和精度的十进制定点算术,但截至目前(Solidity 0.6.2)文档说:
Solidity 尚未完全支持定点数。它们可以声明,但不能分配给或分配自。
因此目前一般不支持定点数和分数数。
那么,下一个问题是:
剧透:你必须效仿他们。
有人会说,256 位应该对任何人都足够了。然而,一旦以太坊中的大多数数字都是 256 位宽,那么即使简单的两个数相加也可能有 257 位宽,而两个数的乘积可能高达 512 位宽。
模拟比编程语言本身支持的类型更宽的固定或可变宽度整数的常见方法是将它们表示为固定或可变长度的较短的、本机支持的整数序列。因此,宽整数的位图是较短整数的位图的串联。
在 Solidity 中,宽整数可以表示为固定或动态数组,其元素是字节或uint值。
对于分数,情况有点复杂,因为它们有不同的风格,每种都有自己的优点和缺点。
最基本的是简单分数:只有一个整数,称为“分子”,除以另一个整数,称为“分母”。在 Solidity 中,简单分数可以表示为一对两个整数,也可以表示为一个整数,其位图是分子和分母位图的串联。在后一种情况下,分子和分母必须具有相同的宽度。
另一种流行的分数格式是定点数。定点数基本上是一个简单的分数,其分母是一个预定义的常数,通常是 2 或 10 的幂。前一种情况称为“二进制”定点,而后者称为“十进制”定点。只要分母是预定义的,就不需要明确指定,所以只需要指定分子。在 Solidity 中,定点数通常表示为单个整数分子,而常用的分母是 10¹⁸、10²⁷、2⁶⁴ 和 2¹²⁸。
另一种众所周知的小数格式是浮点数。基本上,浮点数可以描述如下:

其中m(尾数)和e(指数)是整数,B(基数)是预定义的整数常量,通常为 2 或 10。B =2 的情况称为“二进制”浮点数,B = =10 被称为“十进制”浮点数。
IEEE-754标准化了几种常见的浮点格式,包括称为“半”、“单”、“双”、“四倍”和“八倍”精度的五种二进制格式。这些格式中的每一种都将尾数和指数打包成分别为 16、32、64、128 或 256 位的单个序列。在 Solidity 中,这些标准格式可以用二进制类型bytes2、bytes4、bytes8、bytes16和表示bytes32。或者,尾数和指数可以分别表示为一对整数。
这部分的文件问题:
剧透:没有必要。
好消息是,有各种数字格式的 Solidity 库,例如:fixidity(具有任意小数位数的十进制定点)、DSMath(具有 18 位或 27 位小数的十进制定点)、BANKEX 库(IEEE-754 八元组精度浮点数)、ABDK 库(二进制定点数和四倍精度浮点数)等。
坏消息是不同的库使用不同的格式,所以很难将它们结合起来。这个问题的根源将在下一节中讨论。
在上一节中,我们讨论了数字在运行时是如何表示的。在这里,我们将看看它们在开发时是如何表示的,即在代码本身中。
与主流语言相比,Solidity 具有相当丰富的数字字面量语法。首先,支持良好的旧十进制整数,例如42. 与其他类 C 语言一样,有十六进制整数文字,如0xDeedBeef. 到目前为止,一切都很好。
在 Solidity 中,文字可能有单位后缀,例如6 ether, 或3 days。一个单位,基本上就是一个因数,字面意思是乘以。这ether是 10¹⁸ 和days86,400(24 小时 × 60 分钟 × 60 秒)。
除此之外,Solidity 还支持整数文字的科学记数法,例如2.99792458e8. 这很不寻常,因为主流语言仅支持小数文字的科学记数法。
但可能整个 Solidity 语言最独特的功能是它对有理文字表达式的支持。实际上每个成熟的编译器都能够在编译时计算常量表达式,因此x = 2 + 2不生成add操作码,而是等同于x = 4. Solidity 也能做到这一点,但实际上,它远远不止于此。
在主流语言中,常量表达式的编译时求值只是一种优化,因此常量表达式在编译时的求值方式与在运行时的求值方式完全相同。这使得可以用具有相同值的命名常量或变量替换此类表达式的任何部分,并获得完全相同的结果。然而,对于 Solidity 来说,情况并非如此。
在运行时,Solidity 循环中的除法结果趋向于零,其他算术运算在溢出时回绕,而在编译时,使用具有任意大分子和分母的简单分数计算表达式。因此,在运行时,表达式((7 / 11 + 3 / 13) * 22 + 1) * 39将被计算为 39,而在编译时,相同的表达式被计算为 705。不同之处在于,在运行时,7 / 11和3 / 13被四舍五入为零,但在编译时,整个表达式被计算在简单的分数中,根本没有任何四舍五入。
更有趣的是,以下表达式在 Solidity 中是有效的:7523 /48124631 * 6397,而这不是有效的:7523 / 48125631 * 6397。不同之处在于前者评估为整数,而后者评估为非整数。请记住,Solidity 在运行时不支持分数,因此所有文字都必须是整数。
虽然小数和大整数可以在运行时在 Solidity 中表示,如前几节所述,但没有方便的方法在代码中表示它们。这使得使用此类数字执行操作的任何代码都相当神秘。
只要 Solidity 没有标准的定点或浮点格式,每个库都使用自己的格式,这使得库彼此不兼容。
智能合约从以太坊区块链诞生之初就开始计算。从简单的百分比到复杂的衍生品估值。然而,智能合约开发的主要语言 Solidity 在数学方面并没有走得更远,只是揭示了 EVM 操作码能够做什么。
图书馆试图弥补基本语言遗漏的内容,但缺乏标准化的数字格式。
核心语言有一些无与伦比的特性,但同时缺乏对基本的、必备的东西的支持。
在后续的文章中,我们将讨论如何处理这一切,下一个要讨论的话题就是:溢出
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只
我正在使用active_admin,我在Rails3应用程序的应用程序中有一个目录管理,其中包含模型和页面的声明。时不时地我也有一个类,当那个类有一个常量时,就像这样:classFooBAR="bar"end然后,我在每个必须在我的Rails应用程序中重新加载一些代码的请求中收到此警告:/Users/pupeno/helloworld/app/admin/billing.rb:12:warning:alreadyinitializedconstantBAR知道发生了什么以及如何避免这些警告吗? 最佳答案 在纯Ruby中:classA