掌桥专利:专业的专利平台
掌桥专利
首页

一种静态、动态分析相结合的智能合约状态提取方法

文献发布时间:2024-04-18 20:02:18


一种静态、动态分析相结合的智能合约状态提取方法

技术领域

本申请属于计算机数据信息处理技术领域,尤其涉及一种静态、动态分析相结合的智能合约状态提取方法。

背景技术

近几年区块链技术的崛起为各行业带来了巨大的变革,智能合约在不依赖于可信第三方的情况下可以执行任意的业务逻辑,因而被广泛应用于金融、供应链、医疗和保险等领域。智能合约是一种在区块链某个地址上运行的程序,它们是由数据和函数组成的,可以在收到交易时执行。Solidity应运而生,它是为实现智能合约而创建的高级编程语言,已被广泛使用。智能合约一旦部署在区块链上就不能在同一地址上进行更新升级,但在现实场景中有必要升级智能合约以修复智能合约代码中的漏洞或添加新功能。关键的挑战是不仅要在升级的版本中进行代码更改,还要重用和提取原智能合约中变量存储的数据(状态),但区块链系统并不维护这些状态的存储位置。

为了解决这一问题,研究人员寻求各种解决方案,以准确提取智能合约中的所有状态。在智能合约中,状态变量按照声明的顺序以大小为32字节的槽位(slot)为单位分配存储空间,对于两个相邻但大小小于32字节的状态将被存储在一个slot内。因此,一些静态分析工具和反编译器,接收智能合约的代码或字节码为输入,通过分析以太坊虚拟机(EVM)的存储管理,进而计算状态的存储位置。但是,它们都不能提取键值映射类型(mapping)的状态,这是因为mapping变量的“值”(value)的存储位置是由“键”(key)决定的,key可能是伴随着交易运行动态生成的,因而无法静态获取。为了提取到mapping类型的状态,一些动态分析工具从区块链API中获取并重放所有历史交易,对代码的控制流图进行可达性分析和回溯算法来逼近分析key生成的可能路径,从而获取key的集合及其存储位置。然而,由于API限速和故障等问题,这些工具很难确保快速准确地获取所有历史交易。更重要的是,它们的准确率不能达到100%,这在现实场景中可能意味着用户的财产丢失。因此,急需一种可以准确提取智能合约状态的工具,以应对现实场景中的合约升级以及状态迁移需求。

发明内容

本申请的目的在于克服现有技术的缺点,提供一种静态、动态分析相结合的途径,以实现智能合约全部变量类型的状态提取。该方法使得开发者无需具备任何额外的EVM存储管理知识,开发好的智能合约由静态分析技术自动提取相关状态信息,并且只需要部署一个带有状态提取功能的全节点即可获取任意的状态信息,无需对整个区块链中的所有节点进行分叉升级。

为实现上述目的,本申请技术方案如下:

一种静态、动态分析相结合的智能合约状态提取方法,包括:

采用静态分析工具将智能合约源代码解析为抽象语法树,然后将每个状态的存储布局信息记录到状态记录表中,并将状态记录表与智能合约一同部署到区块链中;

部署了状态记录表的全节点,开启全同步过程监听区块链网络,下载历史区块,读取区块中的交易,识别交易发往的智能合约地址,将相应的状态记录表加载到内存中;

部署了状态记录表的全节点依次运行区块中的所有交易,采用动态分析工具实时维护动态类型状态的存储布局信息,更新状态记录表;

在区块内的所有交易执行完成后,部署了状态记录表的全节点将内存中的状态记录表持久化到数据库中。

进一步地,所述状态记录表采用如下通用的数据结构记录状态的存储布局信息:

状态记录表={变量名,变量类型,{槽位,偏移量},初始槽位,层数,指针}。

进一步地,所述将状态记录表与智能合约一同部署到区块链中,包括:

智能合约通过开发者的账户发送普通交易到区块链,部署在所有全节点中;

状态记录表部署到已开启动态分析服务的全节点中,开发者通过指定已开启动态分析服务的全节点的IP地址和端口号,同时发送一个特殊的交易用于部署状态记录表。

进一步地,所述采用动态分析工具实时维护动态类型状态的存储布局信息,更新状态记录表,包括:

获取操作码,如果是SHA3操作码,则判断SHA3操作码参数的长度是否大于或等于32;

如果SHA3操作码参数的长度大于或等于32,则根据SHA3操作码参数推算出初始槽位,然后查询状态记录表,如果在状态记录表中匹配到初始槽位,则以SHA3操作码计算出的槽位结果更新状态记录表中槽位。

进一步地,所述采用静态分析工具将智能合约源代码解析为抽象语法树,然后将每个状态的存储布局信息记录到状态记录表中,还包括:

将抽象语法树中变量按照声明顺序依次遍历分析;

将变量分为静态类型或动态类型,分别提取对应状态的存储布局信息记录到状态记录表中;

对于复合类型变量,将其解析为静态类型或动态类型,然后分别提取对应状态的存储布局信息记录到状态记录表中。

本申请提出的一种静态、动态分析相结合的智能合约状态提取方法,清晰完整地记录了智能合约中状态的存储空间,为合约升级或数据迁移提供技术支撑。优点在于:开发者无需了解各种类型的变量的存储空间管理机制,直接访问数据库就可以方便快捷的获取到智能合约所有的状态;系统作为外部服务安装在全节点中,确保了所有交易的运行时信息都将被监控识别,因而可以准确识别mapping变量的存储空间;系统的实现只需要在一个全节点上部署服务,对区块链几乎不带来任何开销,利用了全节点同步机制而非使用API获取历史交易,避免了API限速和故障等问题。

附图说明

图1为本申请智能合约状态提取方法流程图。

图2为本申请实施例静态分析流程图。

图3为本申请实施例动态分析流程图。

具体实施方式

为了使本申请的目的、技术方案及优点更加清楚明白,以下结合附图及实施例,对本申请进行进一步详细说明。应当理解,此处描述的具体实施例仅用以解释本申请,并不用于限定本申请。

本申请的一个实施例,提出了一种静态、动态分析相结合的智能合约状态提取方法,如图1所示,包括:

步骤S1、采用静态分析工具将智能合约源代码解析为抽象语法树,然后将每个状态的存储布局信息记录到状态记录表中,并将状态记录表与智能合约一同部署到区块链中。

开发者在本地编写好的智能合约,以.sol为后缀名命名文件。作为输入运行静态分析工具,无需做任何额外工作。

本申请以Solidity编写的智能合约为例,Solidity是用于实现智能合约的一种面向合约的高级编程语言,在Solidity编程的智能合约中,声明在函数体外的变量称之为状态变量,它们的数据将被区块链持久性存储,这些数据也称之为状态。

为了全面处理Solidity编写的智能合约中所有的变量类型,将变量类型分为静态类型、动态类型两类,依据是变量的存储空间是否在合约部署后发生改变,合约部署后变量的存储空间不发生改变的是静态类型,发生改变的是动态类型。

其中,静态类型包括布尔类型、整数类型、地址类型、静态数组、枚举类型等等;动态类型包括动态数组(array、string、byte)和映射类型(mapping)。特别的是存在用户自定义类型和结构体类型(struct),它们是上述静态、动态类型的重命名或组合。

为了全面且准确地维护各种变量类型的状态存储空间,本申请将每个状态的存储布局信息记录到状态记录表中,状态记录表中引入了一种通用的数据结构用于记录状态的存储布局信息:

状态记录表={变量名,变量类型,{槽位,偏移量},初始槽位,层数,指针};

其中,变量名和变量类型用于记录当前状态记录表中维护的是哪个变量的状态,槽位用于记录该变量所分配的存储地址,偏移量用于在多个状态共享一个槽位时该状态在槽位内准确的位置,指针用于处理嵌套动态数组或mapping变量时内层的变量。初始槽位记录了动态类型状态的初始槽,层数是一个用于记录变量的嵌套次数,即由几个基本动态或静态类型组合而成。

在一个具体的实施例中,所述采用静态分析工具将智能合约源代码解析为抽象语法树,然后将每个状态的存储布局信息记录到状态记录表中,还包括:

将抽象语法树中变量按照声明顺序依次遍历分析;

将变量分为静态类型或动态类型,分别提取对应状态的存储布局信息记录到状态记录表中;

对于复合类型变量,将其解析为静态类型或动态类型,然后分别提取对应状态的存储布局信息记录到状态记录表中。

具体地,如图2所示、首先静态分析工具自动解析智能合约的代码并生成抽象语法树(AST),AST以树形结构存储了代码的各个组成部分,并且其可遍历性使得静态分析得以实现。然后,按照变量的声明顺序依次遍历分析每个变量v的名称和类型,将其记录在状态记录表的前两个字段中。

对于静态类型的变量,如果其状态所占空间大小大于或等于32字节,那么可以直接为其分配一个槽位(slot)或多个槽位;如果小于32字节,则通过槽位计算判断该变量的状态是否与其他变量的状态共享一个slot。具体来说,槽位计算通过大小为32字节的栈来实现,每一个不足32字节的变量被压入栈中,如果下一个变量可以继续入栈,则认为它们的状态存储在一个slot中并记录各自的偏移量,否则栈中变量弹出。至此,静态类型的存储空间已经提取完毕,且后续不会发生改变。

对于动态类型的变量,除了变量名和变量类型,额外提取它们的初始槽位和层数,其中初始槽位是其声明位置所占据的slot,层数则是其嵌套的次数。以变量mapping(string=>mapping(uint=>bool))为例,它将被EVM解析为外层的mapping(string=>uint)和内层的mapping(uint=>bool),因而层数为2。在静态分析阶段,状态记录表中只需要记录外层mapping变量的初始槽位,而内层的初始槽位是由动态分析阶段动态维护的。

对于复合类型变量,将其解析为内部基本类型(静态类型或动态类型),然后进行相应的处理。

由静态分析生成的状态记录表与智能合约一同部署到区块链中。智能合约通过开发者的账户发送普通交易到区块链,部署在所有全节点中。区别于智能合约,状态记录表需要部署到特殊的已开启动态分析服务的全节点中。

状态记录表部署时,开发者指定该全节点的IP地址和端口号,同时发送一个特殊的交易用于部署状态记录表。与正常交易类似,特殊交易包含三个字段:“To”指定合约地址,“DATA”包含状态记录表,“FROM”表示开发者的地址。为了将其与普通交易区分开来,“DATA”的开头额外包含一个5字节的标识符(0x0000000022),正常交易的“DATA”字段的前四个字节是外部函数的签名,不能为0,所以可以区分出哪些交易用于部署状态记录表。节点收到该特殊交易后通过查看“FROM”和“To”的部署者是否一致来验证身份。

步骤S2、部署了状态记录表的全节点,开启全同步过程监听区块链网络,下载历史区块,读取区块中的交易,识别交易发往的智能合约地址,将相应的状态记录表加载到内存中。

本实施例部署了状态记录表的全节点,通过本地区块链客户端程序开启全同步过程,开启全同步后该节点将监听区块链网络,下载历史区块。

部署了状态记录表的全节点将读取区块中的所有交易,依据交易结构体中的“TO”字段,可以识别该交易是发往哪个智能合约地址。依据这些合约地址,全节点将相应的状态记录表加载到内存中,等待EVM执行这些交易并同时完成状态记录表的更新。

得益区块链自身的全同步机制,该全节点无需主动调用区块链API以获取历史交易数据,所以不需要考虑数据丢失以及API限速等问题。全同步机制会对区块头中的多种哈希值进行校验以确保区块数据的全局一致性和完整性,因此该节点不会遗漏任何的链上交易,获取到的区块数据与当前区块链最新的数据保持一致。

步骤S3、部署了状态记录表的全节点依次运行区块中的所有交易,采用动态分析工具实时维护动态类型状态的存储布局信息,更新状态记录表。

每下载一个区块体,部署了状态记录表的全节点都需要在虚拟机内部执行区块中的所有交易以完成本地区块链数据库的更新。

在依次运行区块中的所有交易过程中,动态分析工具将实时维护动态类型变量状态的存储布局信息,更新状态记录表。

为了在交易运行时实时维护动态类型的状态的存储空间,一个直接的解决方案是监控Solidity代码中每个赋值语句的整个opcode序列。然而,一个简单的语句将被编译成一段很长的操作码序列。例如,一个string变量的赋值语句(例如,s="1";)在执行流中需要大约200个操作码(opcode)。更糟糕的是,不同的语句会被编译成不同的操作码序列。因此,监视各种类型的复杂操作码序列是极其复杂的。

通过深入研究并发现所有对动态类型变量的存储访问都涉及到一个opcode的执行,即SHA3,它用于计算slot地址。因此,本实施例只需要监视SHA3来发现哪些slot将被某些动态类型的变量访问。

将状态记录表加载到内存中后,该全节点将顺序执行区块中的每笔交易。对于每笔交易,虚拟机EVM都在解释器中顺序执行每个opcode以实现合约代码逻辑的运行。本申请将动态分析嵌入到解释器中,以在交易运行时实时监控状态的存储空间有没有发生改变。

在一个具体的实施例中,采用动态分析工具实时维护动态类型状态的存储布局信息,更新状态记录表,包括:

获取操作码,如果是SHA3操作码,则判断SHA3操作码参数的长度是否大于或等于32;

如果SHA3操作码参数的长度大于或等于32,则根据SHA3操作码参数推算出初始槽位,然后查询状态记录表,如果在状态记录表中匹配到初始槽位,则以SHA3操作码计算出的槽位结果更新状态记录表中槽位。

具体的,如图3所示,在交易运行时,当执行到SHA3(m)这个opcode时(若不是SHA3操作码,则判断下一个操作码),程序对其参数(m)进行校验。如果长度大于32字节,那么当前执行流有可能在访问mapping类型的状态;如果长度等于32字节,那么当前执行流有可能在访问动态数组。背后的原因是,mapping的值(value)的存储位置,是通过SHA3(h(key),初始槽位)计算得出的,此时SHA3参数的长度必定大于32字节;而动态数组的第一个元素的存储位置,是由SHA3(初始槽位)计算得出的,此时SHA3参数的长度必定等于32字节。

根据SHA3操作码参数可以推算出初始槽位,如果m大于32,则按32字节分割key和初始槽位,key是占32字节的倍数,最后一个32字节(或者小于32字节)的就是初始槽位。如果m等于32,则m就是初始槽位。

在获取到可能的初始槽位后,遍历查询状态记录表,如果在状态记录表中匹配到了先前静态分析得到的初始槽位,那么此时SHA3的计算结果便是用于存储的槽位地址,将SHA3的计算结果更新到状态记录表中。如果在状态记录表中未匹配到了先前静态分析得到的初始槽位,则返回获取下一个操作码。

需要说明的是,对于动态数组,SHA3的计算结果只是数组第一个元素的存储位置,还需要依据初始槽位中存储的数组长度,记录数组全部元素所占据的槽位(slot)。

现实中往往存在复杂的嵌套类型,以类型为mapping(string=>mappoing(uint=>bool))的变量m为例,说明动态分析如何识别它的slot。此时解释器正在执行一行赋值语句(m[k

步骤S4、在区块内的所有交易执行完成后,部署了状态记录表的全节点将内存中的状态记录表持久化到数据库中。

持久化过程中,数据需要进行格式转换,以适应数据库表结构。每个表以智能合约的地址命名,表中的每一行代表一个状态变量的状态信息,包括变量名、变量类型、存储位置(包括槽位和偏移量)、初始槽位、层数、指针和区块高度。这些信息都以字符串格式存储在数据库中,并通过指针关联到内层变量所在的行号,以便在需要时能够追踪状态变量之间的关系和历史数据。为了实现快照(snapshot)读取功能,数据库中的每个记录都必须包括区块高度信息。这使得智能合约能够根据区块高度精确地检索特定区块中的状态记录表数据,从而满足合约的历史数据查询需求。

另外,将内存中的状态记录表数据以高效的方式持久化到数据库中同时确保主进程不会因为数据库写入而阻塞,本申请设计了子进程同步机制。即所述将内存中的状态记录表持久化到数据库中,包括:

主进程会创建一个独立的子进程,这个子进程将负责将数据写入数据库,以避免主进程因为数据库写入操作而阻塞。主进程使用操作系统提供的fork()函数来复制自身,创建一个与主进程完全相同的子进程。

子进程独立运行,它接收到主进程的数据副本后,开始将数据写入数据库。这个过程可以是批量写入,也可以是逐条写入,具体取决于系统性能和需求。由于子进程独立于主进程运行,主进程不会因为数据库写入操作而受阻。

数据库更新完成:当子进程成功将数据写入数据库后,它可以向主进程发送一个信号或通知,表明数据已经持久化完成。这种通信机制可以使用进程间通信工具来实现,例如Unix域套接字或管道。

主进程继续:主进程在收到子进程的成功写入通知后,可以继续执行下一批交易或其他任务。由于子进程负责数据持久化,主进程不需要等待数据写入操作完成。

异常处理:如果子进程在写入数据库时遇到错误或异常,它可以向主进程发送错误信息。主进程可以根据错误信息采取适当的措施,例如重试写入、回滚事务,或者通知管理员进行手动干预。

这种子进程fork主进程的设计思想允许数据持久化和智能合约执行并行进行,提高了系统的性能和可用性。主进程能够持续执行智能合约交易,而子进程则负责将内存中的数据高效地写入数据库中,同时还具备异常处理能力。这种设计确保了数据的一致性和可靠性,同时不会让主进程因为数据库写入而受阻。在实际应用中,系统应该定期创建数据库备份,以防止数据丢失。在单节点宕机或数据损坏时,可以使用最近的数据库备份进行恢复,从而减少数据丢失的风险。在将数据持久化到数据库时,可以使用数据库事务来确保数据的完整性。如果在数据写入过程中发生错误,事务可以回滚并保持数据库状态的一致性,避免了部分写入导致的数据不一致性。另外,可以采用一主多从的架构可以提高系统的高可用性。主节点负责同步区块并将状态记录表写入数据库,从节点负责处理用户读取状态记录表的请求。

以上所述实施例仅表达了本申请的几种实施方式,其描述较为具体和详细,但并不能因此而理解为对发明专利范围的限制。应当指出的是,对于本领域的普通技术人员来说,在不脱离本申请构思的前提下,还可以做出若干变形和改进,这些都属于本申请的保护范围。因此,本申请专利的保护范围应以所附权利要求为准。

相关技术
  • 一种基于智能合约的权限可控的智能合约升级方法
  • 一种基于静态与动态分析的智能合约重入漏洞检测方法
  • 区块链智能合约平台支持有状态和无状态合约的处理方法
技术分类

06120116579914