CN109375899B - 一种形式验证Solidity智能合约的方法 - Google Patents
一种形式验证Solidity智能合约的方法 Download PDFInfo
- Publication number
- CN109375899B CN109375899B CN201811119763.2A CN201811119763A CN109375899B CN 109375899 B CN109375899 B CN 109375899B CN 201811119763 A CN201811119763 A CN 201811119763A CN 109375899 B CN109375899 B CN 109375899B
- Authority
- CN
- China
- Prior art keywords
- contract
- statement
- boogie
- expression
- type
- Prior art date
- Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
- Active
Links
Images
Classifications
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F8/00—Arrangements for software engineering
- G06F8/10—Requirements analysis; Specification techniques
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F8/00—Arrangements for software engineering
- G06F8/40—Transformation of program code
- G06F8/41—Compilation
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06Q—INFORMATION AND COMMUNICATION TECHNOLOGY [ICT] SPECIALLY ADAPTED FOR ADMINISTRATIVE, COMMERCIAL, FINANCIAL, MANAGERIAL OR SUPERVISORY PURPOSES; SYSTEMS OR METHODS SPECIALLY ADAPTED FOR ADMINISTRATIVE, COMMERCIAL, FINANCIAL, MANAGERIAL OR SUPERVISORY PURPOSES, NOT OTHERWISE PROVIDED FOR
- G06Q40/00—Finance; Insurance; Tax strategies; Processing of corporate or income taxes
- G06Q40/04—Trading; Exchange, e.g. stocks, commodities, derivatives or currency exchange
Abstract
本发明公开一种形式验证Solidity智能合约的方法,从Solidity智能合约中建立模型,使用Boogie语言对该模型进行描述,然后利用Z3证明器进行验证。本发明的方法设计了一套通用的智能合约模型规范语言,解决了Solidity智能合约和数学模型之间的转化问题,同时对于形式验证其他语言的智能合约也具有重要的参考价值;该方法帮助智能合约的开发者快速、全面、严谨地验证智能合约的功能,定位智能合约的漏洞,保证智能合约的安全。
Description
技术领域
本发明涉及区块链技术、智能合约技术、形式验证、Solidity合约语言、Boogie模型语言、Z3定理证明器,尤其涉及形式验证Solidity智能合约的方法。
背景技术
区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。2008年由中本聪提出区块链的概念,在随后几年中,成为了电子货币比特币的核心组成部分。到2014年,区块链2.0成为关于去中心化区块链数据库的术语。作为第二代可编程区块链,它的主要特点是允许用户写出更精密和智能的协议,也就是所谓的智能合约。
智能合约允许在没有第三方的情况下进行可信交易,这些交易可追踪且不可逆转。智能合约相对传统合约能自动执行原定协议,减少其他交易成本。但是由于区块链上的智能合约是所有用户都能看见的,并且一旦部署无法篡改,所以如果智能合约本身存在漏洞,那么就可能被人发现并进行攻击。这样的攻击往往难以迅速解决,例如著名的TheDao攻击造成了5000万美元的损失,当维护人员试图解决漏洞时黑客已经成功完成了资金回收。很明显,如何编写安全的智能合约已经成为区块链应用开发者关心的问题。传统的流程测试和静态检验确实是一种发现漏洞的有效手段,但它们只能查找漏洞,却无法证明没有漏洞。
形式验证是一种从数学上完备地证明或验证某一模型是否实现了规范所述功能的方法,它可以成为保障智能合约安全的一个有效手段。形式验证可以对指定描述的所有可能的情况进行验证,借用数学上的方法将程序和规范直接进行比较,发现问题或证明没有问题。形式验证方法分为等价性验证、模型验证和定理证明等。但是等价性验证和定理证明存在转化方案上的困难,而模型验证也存在状态爆炸等问题,因此就目前的技术来看,形式验证对于某些形式的程序仍然无法很好地进行验证。虽然很难设计一个通用的智能合约形式验证方案,但是针对某种特定语言的智能合约,比如Solidity智能合约,是可以在满足其大部分语义和功能的前提下,进行形式验证的。
Solidity是一门面向合约的、为实现智能合约而创建的高级编程语言。这门语言受到了C++、Python和Javascript语言的影响,设计的目的是能在以太坊虚拟机(EVM)上运行。目前这门语言是智能合约开发的主流语言,用它可以在区块链上实现投票、众筹、秘密竞拍(盲拍)、多重签名的钱包以及其他应用。
Boogie是一种模型验证语言,用于描述程序的验证条件。Boogie是一种介于SMT语言和程序语言之间的中间语言,用Boogie语言描述程序验证条件后将转化为SMT语言,然后由SMT证明器进行验证。目前基于Boogie开发的程序语言有Dafny、F*、Koka等。
Z3定理证明器是一种SMT证明器,它可以用于验证逻辑表达式的可满足性。Z3作为一个底层工具,具有一套脚本语言作为输入,而这个脚本符合SMT-LIB2.0标准。一旦输入符合标准的脚本,Z3可以高效的对输入的逻辑表达式进行验证,并输出满足、不满足或未知,如果是可满足的,还可以输出一个满足条件的解。利用Z3这个底层工具,就可以开发出程序的形式验证功能,关键就在于将程序语言一步步等价转化为符合SMT-LIB2.0标准的脚本,而这个过程,本质是将程序抽象成数学模型的过程。
Antlr4是一种语法解析工具,可以通过定义语法文件,实现将某一程序代码转化为语法树,并在该语法树的基础上对程序代码进行处理的功能。
发明内容
本发明的目的是针对Solidity智能合约,提供一种形式验证的实现方案,将程序语言Solidity等价转化为Boogie语言,所谓等价转化是指在转化过程中保持Solidity程序语义和功能的一致性,最终形成的Boogie程序能够忠实地刻画原来Solidity合约所实现的功能。
本发明的目的是通过以下技术方案来实现的:
一种形式验证Solidity智能合约的方法,其特征在于,该方法具体包括如下步骤:
S1:首先利用Anltr4语法设计Solidity Model Language规范,所述的规范包括SML语句规范和SML函数规范,SML语句规范以符号'//@'开头,后跟假设语句或断言语句;SML函数规范以符号'//@'开头,后跟规范体;或者以符号'/*@'开头,后跟规范体,最后以符号'*/'结尾;其中,所述的规范体包括前置条件语句、后置条件语句、修改权限语句;SML循环规范以符号'//@'开头,后跟循环语句。所述的假设语句、断言语句、前置条件语句、后置条件语句、修改权限语句和循环语句都由相应语句的关键字和谓词组成;
S2:按照S1所述的SML语法规范在Solidity智能合约中添加详细的注解规范;
S3:如果智能合约开发者进行形式化验证的智能合约的目标文件依赖其他多个Solidity智能合约文件时,首先将依赖文件和目标文件合并成一个文件;
S4:将S3的合并后的文件中的代码由Solidity转化为Boogie,采用弱化继承、弱化封装、增强多态、去可见性的方式以去除面向对象的语言特性,并将Solidity语法中的类型、语句和表达式转换为Boogie语法中相对应的类型、语句和表达式;
S5:采用S4中同样的方法将S2中为Solidity智能合约添加的注解规范转换为Boogie语法的规范;
S6:将通过S4和S5转换后的带有详细注解规范的Boogie代码保存在文件名后缀为.bpl的文件中;
S7:利用集成Boogie编译器和Z3定理证明器的Boogie工具对S6获得的文件进行验证,获得验证结果;
S8:读取验证结果,并将其解析为更为可读的结果,并将更为可读的结果返回给Solidity智能合约开发者。
进一步地,所述的S3中将依赖文件和目标文件合并成一个文件的具体操作为:
对所有`*.sol`文件,获取文件import信息,将其替换为合约名所对应的源码,确保最终所有文件都不再有import信息,否则重复以上操作直至所有文件不再包含import信息;若合并过程中存在同名的合约、库、接口,则将其按照一定规则修改为不同名的合约、库、接口,同时相应的使用该合约、库、接口处也修改为对应名称;并将以上名称修改操作进行记录,后续步骤能够对以上修改进行反向查找。
进一步地,所述的弱化继承,即采用去继承的方式将父合约中的状态变量声明、结构体定义、构造函数定义、修饰器定义、函数定义和枚举定义写入到子合约中;
所述的去继承的方式是指去掉子合约与父合约语法之间的继承关系,对于父合约中的状态变量声明,若不与子合约中的状态变量产生命名冲突,则直接写入子合约;若存在命名冲突,则按照`父合约名称`+`$`+`相关名称`的方式写入子合约中;
Solidity中结构体定义及枚举定义直接写入子合约当中;
函数定义及构造函数定义,若不存在命名冲突,则直接写入子合约中,当存在命名冲突,则按照`父合约名称`+`$`+`相关名称`的方式进行命名写入子合约中;
修饰器定义,若子合约中无同名修饰器,则将修饰器定义写入子合约中,否则不做操作。
进一步地,所述的弱化封装,即指首先在Boogie中定义指针类型Ref`type Ref=int及范型域类型Field`type Field T,通过堆的方式对Solidity中对象进行管理`varHeap:[Ref]<T>[Field T]T,将所有智能合约中的域都直接暴露在文件内成为状态变量、函数、构造函数、修饰器、结构体、枚举。
进一步地,所述的增强多态包括多态定义和多态实现两步,即在该合约实例c的构造函数中,通过assume语句设定其类型为新定义的Boogie类型contract.Type,构建typeof(c)函数来返回contract.Type;然后找出所有该父合约的子合约,从最底层的子合约开始,依次写出if-else选择语句,通过判断contract.Type来调用对应Solidity合约类型的具体函数;
所述的去可见性,即删除所有可见性修饰符。
进一步地,所述的S4的Solidity语法中的类型的转换具体为如下表所示:
Solidity类型 | Boogie类型 |
int,int256 | int |
uint,uint256 | int |
bool | bool |
byte | int |
bytes | [int]int |
string | [int]int |
address | type Ref |
mapping | mapping |
array | type Ref |
struct | type Ref |
object member | type Field T |
Solidity中int、int256、uint、uint256、byte类型转化为Boogie中的int类型后还要对其进行大小限制;
Solidity中address类型是一个封装了uint成员balance及两个安全函数transfer与send的结构体类型,本质上也是一个对象,此处采用所述的处理弱化封装的方法转化为Boogie中的引用类型。
进一步地,所述的Solidity中的语句分为选择语句、循环语句、跳转语句和基本语句四类,具体的转换如下:
Solidity语法中的选择语句直接对应Boogie中的选择语句;
Solidity语法中的循环语句包括while循环、for循环、dowhile循环,将这三种全部转换成while循环;
对于Solidity语法中的跳转语句,对于continue关键字,goto到最近一个循环语句体内的第一行标签,对于break关键字,直接对应Boogie中的break,对于return关键字,其后的表达式放在return之前转化为赋值语句,然后紧跟return,对于throw关键字,在函数返回参数列表中加入throw:bool where throw==false,在该关键字处将throw赋值为true,然后紧跟return;
对于Solidity语法中的基本语句,如果局部变量定义有初始值,那么在定义变量之后,对该变量进行赋值,此外的其他情况都直接对应Boogie语言相应的表达式。
进一步地,所述的Solidity中的表达式分为计算表达式、成员访问表达式、函数表达式、选择表达式、赋值表达式、删除表达式、布尔表达式和基本表达式;
对于计算表达式,若为自增或自减的表达式,将此表达式分成两种Boogie语句,若为加减乘除和取模运算,则直接和Boogie的计算表达式相对应,然后通过assert规范验证计算之后的值是否溢出了对应类型所能表达的范围;
对于成员访问表达式,利用S4中的弱化继承、弱化封装、增强多态、去可见性的方式进行处理;
对于函数表达式,其中的new表达式转化为调用构造函数,函数调用表达式转化为一个或多个Boogie表达式中的函数调用语句,并替换相应的局部变量名;
对于选择表达式,转化为Boogie中的选择语句;
对于赋值表达式,若是自赋值的简写形式,则明确地写出完整的赋值形式;否则直接对应Boogie的赋值表达式;
对于删除表达式,对应Boogie的赋值表达式,给变量赋予相应类型的零值;
对于布尔表达式和基本表达式,直接和Boogie的相应表达式对应。
进一步地,所述的规范转化即将Solidity源文件中添加的SML注解规范中的`//@`部分去掉,对于谓词表达式采用所述的表达式转化的方法进行转化,即得到Boogie规范。
进一步地,所述的S8的结果解析即读取Boogie形式化验证结果,找到其中所存在的问题,通过追踪验证过程的方式反向定位到Solidity不符合规范代码行,并分析解释造成该错误的原因,将分析结果返回给Solidity智能合约开发者。
本发明的有益效果如下:
本发明的方法设计了一套通用的智能合约模型规范语言,解决了Solidity智能合约和数学模型之间的转化问题,同时对于形式验证其他语言的智能合约也具有重要的参考价值;该方法帮助智能合约的开发者快速、全面、严谨地验证智能合约的功能,定位智能合约的漏洞,保证智能合约的安全。
附图说明
图1是本发明的形式验证Solidity智能合约的方法的流程图;
图2是Solidity智能合约的形式验证结果图。
具体实施方式
下面根据附图和优选实施例详细描述本发明,本发明的目的和效果将变得更加明白,以下结合附图和实施例,对本发明进行进一步详细说明。应当理解,此处所描述的具体实施例仅仅用以解释本发明,并不用于限定本发明。
首先利用Anltr4语法设计Solidity Model Language(SML)规范。该规范用于描述程序的功能,使得证明器可以通过识别该语言对程序进行验证。然后利用anltr4语法解析工具设计证明器,该证明器可以对具有规范的Solidity智能合约进行形式验证,证明器的实现步骤是先将其他依赖合约文件导入到待验证的目标合约,然后对目标合约进行去对象化处理,即采用弱化继承、弱化封装、增强多态等技术,接着将处理后的目标合约转化为Boogie程序,即类型转化、语句转化、表达式转化等方案和SML规范的转化方案,最后将转化好的Boogie程序输出成.bpl文件交由Boogie工具验证,并解析验证结果返回客户。
关于SML规范的设计:SML规范都以紧跟@符号的注释出现在Solidity源代码中,按照其出现的位置可以分为SML语句规范、SML函数规范和SML循环规范,他们分别起到不同的验证意义。SML语句规范以符号'//@'开头,后跟假设语句或断言语句;SML函数规范以符号'//@'开头,后跟规范体;或者以符号'/*@'开头,后跟规范体,最后以符号'*/'结尾;其中,所述的规范体包括前置条件语句、后置条件语句、修改权限语句;SML循环规范以符号'//@'开头,后跟循环语句。所述的假设语句、断言语句、前置条件语句、后置条件语句、修改权限语句和循环语句都由相应语句的关键字和谓词组成,具体如表1所示。
表1SML规范
其次,要按照上述的语法规范在Solidity智能合约中添加详细的注解规范;
语句中的所述条件是一个谓词,而谓词对应Solidity语言中的布尔类型的表达式。为了方便开发者以量词的形式表达,进一步拓展Solidity语言的布尔类型表达式,增加了量词表达式,其定义如下。
'('('forall'|'exist')parameterList'::'expression')' |
其中forall关键字对应数学逻辑上的量词任意,exist关键字对应数学逻辑上的量词存在。
另外,增加了old函数继续拓展Solidity语言的表达式。old函数是为了方便开发者区分出函数执行前的变量和函数执行后的变量,从而可以清楚地定义程序状态的变化。
下面的Solidity智能合约体现了所有SML规范的语句类型。
结合上述合约解释规范的具体意思:函数save有一个入参_data,requires_data>0规范了函数的前置条件,表示在函数save执行前,认为入参_data一定大于0。ensuresdata==_data规范了函数的后置条件,表示当函数save执行结束时,合约Main的状态变量data的值等于入参_data的值。modifies data规范了函数的修改权限,表示该函数只能修改data这一状态变量的值。在函数体内,首先定义了一个局部变量i,假设语句assume i==0告诉了SML假定局部变量i的值为0。下面进入一个循环,循环不变量inviriant i<=_data表示在每次循环开始前,都必须满足i<=_data这一不变量。当循环结束时,有一个断言语句assert i==_data,用来检验是否满足循环结束条件i==_data。最后,令data=_data,使其满足函数的后置条件。
关于形式验证Solidity智能合约的方法:如图1所示,主要包括合并依赖、处理面向对象、Solidity转化成Boogie、验证和结果解析。
Solidity是一门面向对象的智能合约语言,Boogie是一门面向模型的定理证明语言。两者无论从设计理念还是表现形式上都有着很大的区别。需要从语法层面对两者进行形式上的转化,使得在保持原本语义的前提下,实现将Solidity程序转化为模型逻辑的目的。
首先从Solidity文件结构开始。Solidity文件以.sol后缀命名,一份文件即是一份以EOF结束的源代码单元sourceUnit,可以包含任意多个编译指令pragmaDirective,导入指令importDirective和合约定义contractDefinition。源代码单元的语法格式如下。
对每一份Solidity文件,将转化为一份Boogie文件。Boogie文件以.bpl后缀命名。
编译指令用于声明编译器的版本。不同版本的编译器对源代码的编译结果是不同的,将可能造成语义的不同甚至语法的不同,因此只能尽量选择稳定的且被广泛使用的编译器版本提供转化方案,而不支持落后或者不稳定的版本。当前较合适的编译器是0.4大版本,具体以0.4.24版本的Solidity语言为转化对象,将其转化为Boogie语言。一般地,编译器在两个大版本之间(比如0.3和0.4版本)将出现明显的不同,而在同一个版本区间内(比如0.4.1~0.4.25)则不容易出现明显的大改动,因此接受编译指令声明为0.4版本的源代码,而拒绝编译指令不是0.4版本的源代码。
这样接受的编译指令格式如下。
其中版本约束versionConstraint应该在[0.4.0,0.5)的区间内。
编译指令仅用于在转化之前,判断是否接受。如果可以接受,则删除该份Solidity源代码的所有编译指令,然后进行下一步转化;否则不进行转化。
导入指令用于引入其他文件中的源代码。Solidity编译器在处理导入指令时,将需导入的源代码写入目标文件中,实际生成的可执行文件包含了目标文件和所有导入文件的代码或代码片段。因此也采用将源代码写入目标文件的方式来替换掉对应的导入指令。导入指令的语法格式如下。
注意,Solidity语法中标识符Identifier支持包含\$符号,但是为了转化时解决命名冲突等问题,强制要求开发者编写的标识符不能含有\$符号,该符号仅能有系统在转化Solidity语言生成Boogie时出现在Boogie的标识符中。这样限制的目的是为了避免系统在重命名时出现和开发者定义的标识符同名。
由于导入指令支持使用from关键字选择部分导入和使用as关键字取别名,因此写入时需要进行筛选和重命名。根据语法可知,只能从其他文件中导入关键字*或者合约,库,接口的标识符,其中标识符指对应类型的名字。
综上,完成了目标合约文件和其他依赖合约文件的合并。
下面继续介绍面向对象特性的处理。Solidity是专为智能合约开发而设计的语言,合约是其最重要的逻辑部分。Solidity合约定义部分包括了对合约contract,接口interface和库library三种类型的定义。
其中表达式expression的具体语法将在之后介绍。合约成员contractPart包括状态变量声明stateVariableDeclaration,使用重载声明usingForDeclaration,结构体定义structDefinition,构造函数定义constructorDefinition,修饰器定义modifierDefinition,函数定义functionDefinition,事件定义eventDefinition,枚举定义enumDefinition。
删除所有事件定义和事件调用语句,因为事件的作用相当于打印日志,并不会对合约的状态变量或合约函数的输出结果造成影响,因此选择忽略对事件相关的验证。
由于Solidity是面向对象的,因此支持通过合约类型contract,接口类型interface和库类型以及结构体封装成员,支持使用is关键字进行多重继承,也支持多态。也即一个合约可以继承多个合约或接口。Boogie语言不支持面向对象,因此需要解决继承、封装和多态在Boogie语言中的表达问题。当然此时不需要关心库类型。
对于继承,本发明采用去继承的方式,即将父合约中一部分合约成员写入到子合约中,写入的成员是父合约中的状态变量声明、结构体定义、构造函数定义、修饰器定义、函数定义和枚举定义。注意,需要将私有的状态变量声明、构造函数定义和函数定义也写入到子合约中,虽然实际上子合约不应该继承父合约的私有成员,但是为了顾及如子合约调用父合约方法访问父合约的私有状态变量这些情况,需要这么做。写入后删除关键字is和其后的继承列表。首先将所有接口类型interface删除,同时删除所有继承该接口的合约中的声明部分;对于所有抽象合约,删除其上的关键字abstract,删除其中的所有抽象方法。然后按照这样的流程执行写入,直到所有合约没有is关键字:按合约定义顺序遍历所有合约,如果当前合约有is关键字,从继承列表的最后一个父合约往前遍历,如果当前父合约没有is关键字,则将该父合约写入子合约,删除继承列表中该父合约。
将父合约写入子合约时,需要注意对几种情况进行特殊处理。对于函数定义,如果子合约中有父合约的同名函数sameFunction,将父合约FatherContract中函数名sameFunction转化为FatherContract\$sameFunction写入子合约,同时,子合约中可能通过FatherContract.sameFunction的形式调用父合约中的函数,应该把这个调用也转化为FatherContract\$sameFunction;否则直接写入。对于状态变量声明,如果子合约中有父合约的同名状态变量sameName,将父合约FatherContract中变量名sameName转化FatherContract\$sameName写入子合约,同时,子合约中可能通过FatherContract.sameName的形式引用了父合约中的变量,应该把这个变量也转化为FatherContract\$sameName;否则直接写入。对于结构体定义,直接写入即可。对于构造函数定义,在子合约的构造函数第一行还开始,按继承顺序从上往下插入父合约的构造函数;父合约构造函数的形式从继承列表或者子合约构造函数的修饰器中确定;将所有父合约构造函数写入子合约,将父合约和子合约的构造函数转化为以给自合约名为函数名的函数形式。对于修饰器定义,如果子合约中有父合约的同名修饰器,则父合约中的修饰器不必写入;否则直接写入。对于枚举定义,直接写入即可。
如果合约中存在super关键字,则需要对super关键字进行转化。对每个合约,根据其继承声明,可以整理出一条以自己作为终节点的按关键字is定义的继承顺序从后往前追溯的继承线,合约方法中的super.m这样转化:设该方法m原本来源于合约A,A在继承线中往上追溯父合约S,如果S中存在同名方法m,则super.转化为S\$。
如果合约中存在this关键字,直接删除this.即可。
现在每份.sol文件都不再依赖导入,文件内每个合约类型(指合约类型contract和库类型library,接口类型interface和抽象合约已经被处理掉了)也不再依赖继承。每一份.sol文件都将包若干个合约类型,这些类型都被独立的封装在合约类型内,然而Boogie语言不支持面向对象的封装特性,采用解封的方式进行类型转化,即一个文件内不存在对象封装,所有合约中的域都直接暴露在文件内成为状态变量、函数、构造函数、修饰器、结构体、枚举。
一个对象将在Boogie中定义为一种类型Ref,它本质是对象的地址,因此它可以直接作为int的别名。对象中的域定义为一种泛型类型Field T,其中T是一种范型,对应该域的具体类型,它可能是int、bool或者Ref等。通过全局变量Heap来模拟对象的管理。Heap是一个以Ref和Field T作为键,T作为值的映射。再通过全局变量alloc来记录Ref的地址分配。对应的Boogie代码如下。
通过上述两个类型和两个全局变量,可实现将封装这一机制弱化为通过全局上的变量管理。转化本质上通过Boogie模拟Solidity程序对变量在堆中管理和存储的过程。特别地,在构造函数中初始化了合约中的状态变量,对于结构体或数组,将对每个成员或元素赋予初值。
对于每个对象,通过构造函数,动态地分配一个地址。分配地址通过一个递增的alloc实现,这样可以保证每个对象获得地址都是唯一不重复的。
Solidity语法没有提供根据合约实例获取其类型的方法,因此无法主动根据合约实例类型来进行不同的操作。但是需要通过规范来描述实例是不同类型时满足的后置条件,因此SML需要提供一个typeof(c)函数来返回合约实例c的类型,来增强对于多态的表达能力。
通过定义typeof函数,为SML规范提供了根据实例获得实例类型的功能。对于实例对应的类型,在其构造函数中通过assume语句设定。
类型是一个新定义了Boogie类型contract.Type,它的实例支持定义偏序来体现Solidity中继承关系。开发者可以通过运算符<:来判断每个对象示例类型之间的继承关系。
对于多态的实现,将根据父合约的类型,找出所有该父合约的子合约,从最底层的子合约开始,依次写出if-else语句,通过判断类型来调用对应类型的具体方法。
实际上,实现了去继承和去封装后,文件内的一切都直接暴露出来,可见性修饰符已经不起作用了,因此直接删除所有可见性修饰符。
在完成面向对象的转化之后,智能合约代码已经转化为只有全局变量、全局常量、函数、公理、类型、程序点的Boogie伪程序,所谓伪程序,说明还没有完全转化符合Boogie语法的程序,需要对细节进行进一步转化。需要把合约中所有域以及每个语句和表达式作更细致的转化。
状态变量的语法如下所示。
对于状态变量的定义,只需要针对不同类型进行不同的转化即可,同时对于具有初始值的状态变量将在构造函数中赋予初始值。而变量名已经在弱化封装时进行了处理,即带上合约名前缀,状态变量的修饰符中关于可见性的修饰符可以直接去掉,常量修饰符也可以直接去掉。
对于基本类型elementaryTypeName,考虑address、bool、string、int、int256、uint、uint256、byte、bytes、bytes32这10种类型,而其他基本类型,将认为不安全的类型不予验证。对于int和int256类型,它表示256字节的符号整数,而Boogie中的int类型是数学意义上的整数,理论上数字大小是无限的。因此可以直接将int和int256类型转化为Boogie中的int类型,再对其进行大小限制。对于uint和uint256类型,它表示256字节的无符号整数,同对int和int256类型的处理,转化为Boogie中int类型后,再对其进行大小限制即可。对于bool类型,直接对应Boogie中的bool类型。对于byte类型,直接转化为Boogie中的int类型,再对其进行大小限制。对于bytes和string,转化为字节数组。对于address类型,由于它封装了uint成员balance和两个安全函数transfer和send,通过结构体的形式定义address类型,结构体类型本质也是一个对象,采用处理面向对象的方法即可。关于数组如何转化将在下面描述。这样将基本类型都已经处理完毕。
对于自定义类型,考虑合约类型contract和结构体类型,采用处理面向对象的方法即可。对于mapping类型,直接对应Boogie中的映射类型。对于数组直接对应键是int类型的Boogie中的映射类型。
综上,类型转化可以总结为下表。
下面介绍Solidity语句转化方案。语句的语法定义如下。
上述语法中,不支持inlineAssemblyStatement。另外,由于已经忽略了事件的定义,所以emitStatement也将同样忽略。对于除了选择、循环语句中出现的block,在程序中出现的局部block不支持。
对于选择语句,直接对应Boogie中的选择语句即可。
在Boogie语法中,仅支持while循环的语法,因此对于Solidity语法中的while循环可以直接对应。
对于for循环,本质上是可以转化为while循环的:将for循环括号中的simpleStatement放在while循环开始之前,将for循环括号中的第一个expression放在while括号中,将for循环括号中的第二个expression放在while循环块中的最后。
对于dowhile循环,本质上也可以转化为while循环的:将do循环块放在while循环之前,将expression放在while循环括号中,将do循环块作为while循环块。
Boogie语法中提供了return、break和goto语句,事实上,通过goto和return就可以实现Solidity中的四种调转语句。
对于continue关键字,goto到最近一个循环语句体内的第一行标签,对于break关键字,直接对应Boogie中的break,对于return关键字,其后的表达式放在return之前转化为赋值语句,然后紧跟return,对于throw关键字,在函数返回参数列表中加入throw:boolwhere throw==false,在该关键字处将throw赋值为true,然后紧跟return;
基本语句包括局部变量声明和表达式。局部变量定义如果仅是定义变量,Boogie语法可以直接对应,如果有初始值,那么在定义变量之后,对该变量进行赋值即可。
最后完成对表达式的转化设计。Solidity表达式的语法如下。
对于自增或自减的表达式expression('++'|'--')和('++'|'--')expression,需要将此表达式分成两种Boogie语句,一种是expression自身,一种对expression赋值。若是expression('++'|'--'),则赋值语句放在expression语句之后;若是('++'|'--')expression,则赋值语句放在expression语句之前。
对于计算表达式,除了幂运算表达式expression'**'expression不支持,其他加减乘除和取模运算都可以直接和Boogie的计算表达式相对应。
注意,对于不同类型的计算,需要考虑溢出问题,在计算之后,通过assert规范验证计算之后的值是否溢出了对应类型所能表达的范围。
对于正负表达式('+'|'-')expression,直接和Boogie的正负表达式对应。
对于位运算表达式本发明不支持。
对于地址访问表达式expression'['expression']',直接和Boogie的映射访问键对应。
对于成员访问表达式expression'.'identifier,同样采用弱化继承、弱化封装、增强多态、去可见性的方式进行处理,用Heap取出相应的成员即可。
对于函数调用表达式expression'('functionCallArguments')',直接和Boogie的函数调用对应。
对于new表达式'new'typeName,事实上是调用了构造函数,如果是合约,则直接调用合约的构造函数,如果是数组,则调用数组的构造函数。
对于三元选择表达式,转化为Boogie中的ifelse语句。
对于逻辑表达式'!'expression、expression'&&'expression、expression'||'expression,直接和Boogie的逻辑表达式对应。
对于比较表达式,直接和Boogie的比较表达式对应。
对于赋值表达式,若是自赋值的简写形式,则明确地写出完整的赋值形式;否则直接对应Boogie的赋值表达式。
对于删除表达式('after'|'delete')expression,不支持after关键字;对delete关键字,对应Boogie的赋值表达式,即给expression赋予相应类型的零值。
对于基本表达式,对应上面章节中对类型的处理方案。
另外,对于括号表达式'('expression')',直接和Boogie的括号表达式对应。
为阐述方便,在前文提及的计算表达式包括了此处所述的自增或自减表达式、正负表达式、位运算表达式和计算表达式,成员访问表达式包括了此处所述的地址访问表达式和成员访问表达式,函数表达式包括了此处所述的函数调用表达式和new表达式,基本表达式包括了此处所述的基本表达式和括号表达式。
综上,完成了Solidity转Boogie的所有细节介绍。现在证明器已经能够生成Boogie程序,然后交由Boogie用Z3证明器进行形式验证。最后,需要把Boogie的验证结果解析成对应原来Solidity合约的可读的结果。解析的过程其实是Solidity转化成Boogie过程的逆过程,由于转化是等价的,因此只需每一步记录转化过程,就可以追溯回去,完成结果的解析。
如图2所示,用户用本发明设计的SML规范和证明器,可以获得快捷方便的Solidity合约形式验证服务。
本发明的形式验证Solidity智能合约的证明器包括如下内容:
1)定义并实现证明器的调用接口,以一份.sol后缀的Solidity智能合约和一个包含其依赖的文件夹路径作为入参,返回验证结果。该接口是证明器的主接口,开发者调用该接口对智能合约进行形式验证;
2)定义并实现验证Solidity语法接口,以.sol后缀的Solidity智能合约作为入参,返回编译结果。该接口的功能是验证开发者所提交的智能合约是否符合语法规范,如果不符合则反馈语法错误,不再进行验证;
3)定义并实现合并Solidity依赖接口,以一份.sol后缀的Solidity智能合约和一个包含其依赖的文件夹路径作为入参,返回一份合并后的目标文件。该接口的功能是处理合约的导入指令,解决循环依赖和别名的问题;
4)定义并实现去对象接口,包括弱化继承、弱化封装、增加多态等接口,以目标文件作为入参,以处理后的目标文件作为返回参数。该接口的功能是将Solidity的面向对象特性去除,往函数式方向转化,以更接近Boogie的语言特点;
5)定义并实现Solidity语言转Boogie的模型接口,包括类型转化、语句转化、表达式转化等,以目标文件作为入参,以Boogie程序对象作为返回参数。该接口的功能是将Solidity语言转化为模型描述语言Boogie;
6)定义并实现语法树监听器,以目标文件作为入参,以Boogie程序对象为返回参数。该监听器是对用上一步模型接口的组件,有多个监听器以实现各个语法节点的具体转化;
7)定义并实现Boogie程序对象及其输出程序的功能,以Boogie程序对象作为入参,以程序字符串作为返回参数。程序对象本质是一颗Boogie语法树,通过继承和组合的关系连接各个语法节点,通过重载toString方法实现打印程序字符串的功能;
8)定义并实现Boogie程序验证接口,以Boogie程序对象作为入参,以验证结果作为返回参数。该接口的功能是对用Boogie语言描述地Solidity合约模型进行形式验证;
9)定义并实现验证结果的解析接口,以Boogie验证结果作为入参,以解析结果作为返回参数。该接口的功能是将验证结果解释为客户可以理解的描述性语言,方便开发者准确定位合约的问题。
本领域普通技术人员可以理解,以上所述仅为发明的优选实例而已,并不用于限制发明,尽管参照前述实例对发明进行了详细的说明,对于本领域的技术人员来说,其依然可以对前述各实例记载的技术方案进行修改,或者对其中部分技术特征进行等同替换。凡在发明的精神和原则之内,所做的修改、等同替换等均应包含在发明的保护范围之内。
Claims (10)
1.一种形式验证Solidity智能合约的方法,其特征在于,该方法具体包括如下步骤:
S1:首先利用Anltr4语法设计Solidity Model Language规范,所述的规范包括SML语句规范 、SML函数规范和SML循环规范,SML语句规范以符号'//@'开头,后跟假设语句或断言语句;SML函数规范以符号'//@'开头,后跟规范体;或者以符号'/*@'开头,后跟规范体,最后以符号'*/'结尾;所述的规范体包括前置条件语句、后置条件语句、修改权限语句;SML循环规范以符号'//@'开头,后跟循环语句; 所述的假设语句、断言语句、前置条件语句、后置条件语句、修改权限语句和循环语句都由相应语句的关键字和谓词组成;
S2:按照S1所述的SML语句 规范在Solidity智能合约中添加详细的注解规范;
S3:如果智能合约开发者进行形式化验证的智能合约的目标文件依赖其他多个Solidity智能合约文件时,首先将依赖文件和目标文件合并成一个文件;
S4:将S3的合并后的文件中的代码由Solidity转化为Boogie,采用弱化继承、弱化封装、增强多态、去可见性的方式以去除面向对象的语言特性,并将Solidity语法中的类型、语句和表达式转换为Boogie语法中相对应的类型、语句和表达式;
S5:采用S4中同样的方法将S2中为Solidity智能合约添加的注解规范转换为Boogie语法的规范;
S6:将通过S4和S5转换后的带有详细注解规范的Boogie代码保存在文件名后缀为.bpl的文件中;
S7:利用集成Boogie编译器和Z3定理证明器的Boogie工具对S6获得的文件进行验证,获得验证结果;
S8:读取验证结果,并将其解析为更为可读的结果,并将更为可读的结果返回给Solidity智能合约开发者。
2.根据权利要求1所述的形式验证Solidity智能合约的方法,其特征在于,所述的S3中将依赖文件和目标文件合并成一个文件的具体操作为:
对所有`*.sol`文件,获取文件import信息,将其替换为合约名所对应的源码,确保最终所有文件都不再有import信息,否则重复以上操作直至所有文件不再包含import信息;若合并过程中存在同名的合约、库、接口,则将其按照一定规则修改为不同名的合约、库、接口,同时相应的使用该合约、库、接口处也修改为对应名称;并将以上名称修改操作进行记录,后续步骤能够对以上修改进行反向查找。
3.根据权利要求1所述的形式验证Solidity智能合约的方法,其特征在于,所述的弱化继承,即采用去继承的方式将父合约中的状态变量声明、结构体定义、构造函数定义、修饰器定义、函数定义和枚举定义写入到子合约中;
所述的去继承的方式是指去掉子合约与父合约语法之间的继承关系,对于父合约中的状态变量声明,若不与子合约中的状态变量产生命名冲突,则直接写入子合约;若存在命名冲突,则按照`父合约名称`+`$`+`相关名称`的方式写入子合约中;
Solidity中结构体定义及枚举定义直接写入子合约当中;
函数定义及构造函数定义,若不存在命名冲突,则直接写入子合约中,当存在命名冲突,则按照`父合约名称`+`$`+`相关名称`的方式进行命名写入子合约中;
修饰器定义,若子合约中无同名修饰器,则将修饰器定义写入子合约中,否则不做操作。
4.根据权利要求1所述的形式验证Solidity智能合约的方法,其特征在于,所述的弱化封装,即指首先在Boogie中定义指针类型type Ref=int及范型域类型type Field T,通过堆的方式对Solidity中对象进行管理var Heap:[Ref]<T>[Field T]T,将所有智能合约中的域都直接暴露在文件内成为状态变量、函数、构造函数、修饰器、结构体、枚举。
5.根据权利要求1所述的形式验证Solidity智能合约的方法,其特征在于,所述的增强多态包括多态定义和多态实现两步,即在该合约实例c的构造函数中,通过assume语句设定其类型为新定义的Boogie类型contract.Type,构建typeof(c)函数来返回contract.Type;然后找出所有该父合约的子合约,从最底层的子合约开始,依次写出if-else选择语句,通过判断contract.Type来调用对应Solidity合约类型的具体函数;
所述的去可见性,即删除所有可见性修饰符。
7.根据权利要求1所述的形式验证Solidity智能合约的方法,其特征在于,所述的Solidity中的语句分为选择语句、循环语句、跳转语句和基本语句四类,具体的转换如下:
Solidity语法中的选择语句直接对应Boogie中的选择语句;
Solidity语法中的循环语句包括while循环、for循环、dowhile循环,将这三种全部转换成while循环;
对于Solidity语法中的跳转语句,对于continue关键字,goto到最近一个循环语句体内的第一行标签,对于break关键字,直接对应Boogie中的break,对于return关键字,其后的表达式放在return之前转化为赋值语句,然后紧跟return,对于throw关键字,在函数返回参数列表中加入throw:bool where throw==false,在该关键字处将throw赋值为true,然后紧跟return;
对于Solidity语法中的基本语句,如果局部变量定义有初始值,那么在定义变量之后,对该变量进行赋值,此外的其他情况都直接对应Boogie语言相应的表达式。
8.根据权利要求1所述的形式验证Solidity智能合约的方法,其特征在于,所述的Solidity中的表达式分为计算表达式、成员访问表达式、函数表达式、选择表达式、赋值表达式、删除表达式、布尔表达式和基本表达式;
对于计算表达式,若为自增或自减的表达式,将此表达式分成两种Boogie语句,若为加减乘除和取模运算,则直接和Boogie的计算表达式相对应,然后通过assert规范验证计算之后的值是否溢出了对应类型所能表达的范围;
对于成员访问表达式,利用S4中的弱化继承、弱化封装、增强多态、去可见性的方式进行处理;
对于函数表达式,其中的new表达式转化为调用构造函数,函数调用表达式转化为一个或多个Boogie表达式中的函数调用语句,并替换相应的局部变量名;
对于选择表达式,转化为Boogie中的选择语句;
对于赋值表达式,若是自赋值的简写形式,则明确地写出完整的赋值形式;否则直接对应Boogie的赋值表达式;
对于删除表达式,对应Boogie的赋值表达式,给变量赋予相应类型的零值;
对于布尔表达式和基本表达式,直接和Boogie的相应表达式对应。
9.根据权利要求8所述的形式验证Solidity智能合约的方法,其特征在于,所述的规范转化即将Solidity源文件中添加的SML注解规范中的`//@`部分去掉,对于谓词表达式采用所述的表达式转化的方法进行转化,即得到Boogie规范。
10.根据权利要求1所述的形式验证Solidity智能合约的方法,其特征在于,所述的S8的结果解析即读取Boogie形式化验证结果,找到其中所存在的问题,通过追踪验证过程的方式反向定位到Solidity不符合规范代码行,并分析解释造成该错误的原因,将分析结果返回给Solidity智能合约开发者。
Priority Applications (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN201811119763.2A CN109375899B (zh) | 2018-09-25 | 2018-09-25 | 一种形式验证Solidity智能合约的方法 |
Applications Claiming Priority (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN201811119763.2A CN109375899B (zh) | 2018-09-25 | 2018-09-25 | 一种形式验证Solidity智能合约的方法 |
Publications (2)
Publication Number | Publication Date |
---|---|
CN109375899A CN109375899A (zh) | 2019-02-22 |
CN109375899B true CN109375899B (zh) | 2021-08-03 |
Family
ID=65401797
Family Applications (1)
Application Number | Title | Priority Date | Filing Date |
---|---|---|---|
CN201811119763.2A Active CN109375899B (zh) | 2018-09-25 | 2018-09-25 | 一种形式验证Solidity智能合约的方法 |
Country Status (1)
Country | Link |
---|---|
CN (1) | CN109375899B (zh) |
Families Citing this family (19)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN109829825A (zh) * | 2019-04-03 | 2019-05-31 | 浙江师范大学 | 基于交易源认证器的智能合约的安全函数设计方法及系统 |
WO2021018312A1 (zh) * | 2019-07-30 | 2021-02-04 | 杭州趣链科技有限公司 | 基于区块链的场外交易方法、系统、存储介质和电子装置 |
CN110533533A (zh) * | 2019-08-21 | 2019-12-03 | 杭州趣链科技有限公司 | 一种场外交易智能合约的形式验证方法 |
CN110471921A (zh) * | 2019-08-08 | 2019-11-19 | 杭州复杂美科技有限公司 | 一种智能合约配置、调用和更新方法、设备及存储介质 |
CN110543407B (zh) * | 2019-08-21 | 2021-11-05 | 杭州趣链科技有限公司 | 一种Solidity智能合约性能静态分析方法 |
CN110555320B (zh) * | 2019-08-21 | 2021-03-23 | 杭州趣链科技有限公司 | 一种基于区块链存证智能合约的形式验证方法 |
CN110705974B (zh) * | 2019-09-03 | 2022-07-05 | 杭州趣链科技有限公司 | 一种完备的智能合约形式规范实现方法 |
CN110633076B (zh) * | 2019-09-16 | 2021-05-04 | 杭州趣链科技有限公司 | 一种自动生成Solidity智能合约Java客户端程序的方法 |
CN110737899B (zh) * | 2019-09-24 | 2022-09-06 | 暨南大学 | 一种基于机器学习的智能合约安全漏洞检测方法 |
CN110929295B (zh) * | 2019-11-08 | 2021-11-05 | 杭州趣链科技有限公司 | 一种投票智能合约的形式验证方法 |
CN111124485B (zh) * | 2019-12-20 | 2023-03-10 | 成都互诚在线科技有限公司 | 一种基于中间语言的形式化规约语言简化方法 |
CN111124422B (zh) * | 2019-12-25 | 2023-03-10 | 成都互诚在线科技有限公司 | 一种基于抽象语法树的eos智能合约语言转换方法 |
CN111176625B (zh) * | 2019-12-31 | 2023-07-28 | 杭州趣链科技有限公司 | 一种在浏览器端高效编译Solidity智能合约的方法 |
CN112256271B (zh) * | 2020-10-19 | 2022-11-29 | 中国科学院信息工程研究所 | 一种基于静态分析的区块链智能合约安全检测系统 |
CN112581140B (zh) * | 2020-12-24 | 2022-07-29 | 西安深信科创信息技术有限公司 | 一种智能合约验证方法、计算机存储介质 |
CN112613043B (zh) * | 2020-12-30 | 2024-02-27 | 杭州趣链科技有限公司 | 一种基于智能合约调用网络的智能合约漏洞检测方法 |
CN113220704B (zh) * | 2021-07-01 | 2022-04-26 | 支付宝(杭州)信息技术有限公司 | 在区块链中部署、查询和执行智能合约的方法及装置 |
CN113835688B (zh) * | 2021-08-27 | 2024-04-12 | 河南理工大学 | 一种科学计算语言解释器的对象封装方法 |
CN116777620A (zh) * | 2023-06-25 | 2023-09-19 | 北京航空航天大学 | 一种确保资产证券化合约可靠性的形式化验证方法 |
Citations (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN104503816A (zh) * | 2014-12-30 | 2015-04-08 | 西安电子科技大学 | 一种硬件语言vhdl到msvl的自动转换系统 |
CN108459860A (zh) * | 2018-03-28 | 2018-08-28 | 成都链安科技有限公司 | 区块链智能合约形式化验证代码转换器及转换方法 |
Family Cites Families (1)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
WO2018049203A1 (en) * | 2016-09-09 | 2018-03-15 | MonetaGo Inc. | Asset exchange system and method |
-
2018
- 2018-09-25 CN CN201811119763.2A patent/CN109375899B/zh active Active
Patent Citations (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN104503816A (zh) * | 2014-12-30 | 2015-04-08 | 西安电子科技大学 | 一种硬件语言vhdl到msvl的自动转换系统 |
CN108459860A (zh) * | 2018-03-28 | 2018-08-28 | 成都链安科技有限公司 | 区块链智能合约形式化验证代码转换器及转换方法 |
Non-Patent Citations (1)
Title |
---|
智能合约的形式化验证方法;胡凯,白晓敏等;《信息安全研究》;20161205;全文 * |
Also Published As
Publication number | Publication date |
---|---|
CN109375899A (zh) | 2019-02-22 |
Similar Documents
Publication | Publication Date | Title |
---|---|---|
CN109375899B (zh) | 一种形式验证Solidity智能合约的方法 | |
Grech et al. | Gigahorse: thorough, declarative decompilation of smart contracts | |
US7117488B1 (en) | Safe computer code formats and methods for generating safe computer code | |
US8954939B2 (en) | Extending a development environment | |
Swamy et al. | Verifying higher-order programs with the Dijkstra monad | |
US7818729B1 (en) | Automated safe secure techniques for eliminating undefined behavior in computer software | |
US20210081185A1 (en) | System and method for compiling high-level language code into a script executable on a blockchain platform | |
US20240020109A1 (en) | Method and system for supporting smart contracts in a blockchain network | |
Kasampalis et al. | Language-parametric compiler validation with application to LLVM | |
Pandey et al. | LLVM cookbook | |
Olsson | PHP 7 Quick Scripting Reference | |
US20170075668A1 (en) | Methods and Systems for Generating Client-Server Applications for Target Devices | |
WO2020041411A2 (en) | Object oriented smart contracts for utxo-based blockchains | |
Khoo | Decompilation as search | |
Robbins et al. | From minx to minc: Semantics-driven decompilation of recursive datatypes | |
Metere et al. | Sound transpilation from binary to machine-independent code | |
Leino et al. | Dafny reference manual | |
Beringer et al. | Verifying pointer and string analyses with region type systems | |
Li et al. | Spoq: Scaling {Machine-Checkable} Systems Verification in Coq | |
Cheng et al. | Tolerating C integer error via precision elevation | |
Fehnker et al. | Model checking dataflow for malicious input | |
Walton | Abstract Machines for Dynamic Computation | |
Walther | Deobfuscating JavaScript | |
Hybel | Decompilation of Ethereum smart contracts | |
Miranti | Semantics of low-level languages |
Legal Events
Date | Code | Title | Description |
---|---|---|---|
PB01 | Publication | ||
PB01 | Publication | ||
SE01 | Entry into force of request for substantive examination | ||
SE01 | Entry into force of request for substantive examination | ||
GR01 | Patent grant | ||
GR01 | Patent grant |