hi 欢迎来到小秘课堂第七期,今天我们来讲讲智能合约的一种设计结构的那些事儿,欢迎主讲人冯开开
讲师:冯开开
编辑:Leo
前言
智能合约的设计和传统的应用设计有点不同。传统应用一般为了快速迭代是在产品之后考虑安全,但是 DApp 则需要在产品出来之前就考虑安全问题,它将会关系到账户资产、用户数据等问题,而且对 DApp 来讲,升级是个比较麻烦的事情,因此在智能合约设计时,结构是非常重要的部分。
目前 DApp 面临问题
首先,是关于 DApp 和 App。事物发展将会遵循技术为王、产品为王、最后到运营为王三个发展阶段。现在,区块链和 DApp 正处于技术为王阶段。整个市场上的 DApp,在性能和用户友好性上,都不如 App。DApp 的优势显而易见:去中心化,它是依附区块链的应用。但是我们认为很多 DApp 的短板,其实是因为底层区块链的限制。
其次,是关于安全。现在 DApp 爆发的安全漏洞很多,主要原因是区块链仍处于发展早期。开发 DApp 的基础设施和相关工具都很不成熟,但是黑客是很成熟的,在互联网上久经沙场,对 DApp 世界影响很大。所以,在设计 DApp 时,要了解区块链相关知识,这些是出于安全考虑。
最后,是关于成本。在以太坊中就是 Gas,部署智能合约将消耗一定 Gas。这是因为 DApp 很消耗 Gas,特别是部署一个大型 DApp(包括后面的维护、升级)。Gas 是什么?是资金。那么,有没有一种结构能够暂时忽略 Gas。这就分成两种方向,一是思考节约 gas 到细微处,用一种怪异不太舒服的写法来节约 Gas;第二种是走向宏观,整个结构是清晰明了的,但是可能会存在浪费 Gas 的行为。
解决办法
第一,是优美结构。一个优美的结构会带来 Gas 的节约,这是我一直相信的。那整个结构是包含哪些方面呢?最宏观的说指分层。这里分层和一般的 app 分层是相通的,比如应用层、逻辑层、数据层。
第二,是友好。因为现在一个大型的 DApp,如果有很好的模块化,可能会有十几个智能合约,他们中间可能还有依赖。那就要求你在部署时,需要格外小心。(友好就是说能够一键部署。 )
第三,是支持升级。这个是在 App 上很常见的,因为我们在开发一个 App 时,必须要进行版本迭代,新功能的增加,Bug fix...这个就是升级。而我们知道 DApp 在区块链上,由于区块链的不可篡改性,部署的合约就在那里了。这里面涉及升级的问题就比较复杂。简而言之,我们这个结构采取了支持升级的方式,升级比较简单的部分是算法部分,算法部分是纯粹的逻辑,替换可以做到无感知,只需要修改逻辑合约的地址即可。比较麻烦的是对数据结构的升级,数据结构涉及到实际数据,如果轻易改动就要兼容以前的数据(这种需求很常见)。数据迁移是一个比较麻烦的事情,在设计数据结构之初尽量确定数据结构,避免频繁的变动。数据结构确定之后,包括 CURD 以及一些 Check 的接口其实也可以确定。这些可以作为一个最小的数据合约单元。
第四,是访问控制。要对整个结构中的数据流转进行控制,这是出于安全性的考虑。
第五,是模块化。一方面,是出于安全考虑,所有东西在一个合约里会增加合约的复杂性,会对安全造成隐患。毕竟复杂是安全的天敌,越简单越安全,这是软件开发中的常识。所以我们要保证合约的简洁,一眼看过去就知道这个合约是干什么。另一方面,是保证函数的简单。一样的道理,函数简单意味着组成的合约也是简单的。
整个分层就是应用层、逻辑层和数据层,这个结构里面的三层,整个 DApp 是偏向后端的,这里三层是智能合约里面的三层。
结构设计
根据之前 hackathon 上做的项目,叫做 summerWar(区块链沙盒游戏),这个项目里的结构基本上遵循这种设计。
summer War GitHub 仓库地址:
https://github.com/CryptapeHackathon/SummerWars
an Arch : layer
这个是我们整个游戏的结构图,最左边的是 off-chain,是关于链下用户操作。register 是应用层、Permission 是访问控制、后面是逻辑层和数据层,后面是数据流转和调用的图 。
分层:register 是应用层,operator 是单纯逻辑 ,和数据没有关系。后面是数据层,整个数据单独和逻辑拆分。
权限刚才已经阐述了。在整个结构流转中,如果 operator 是操作层的话,用户需要先访问操作层合约,然后由操作层访问底层数据合约。这里限制访问控制,一是指控制各个层之间的调用关系,比如数据类合约严格控制只能让操作类合约来控制,不可能是随便的合约就能访问的。这里数据不仅是指数据,还包括数据的简单存取接口,相当于一个数据库的概念,包含存储和查询。 二是可以控制具体用户的操作。
An arch: auth
auth 模块实现了上述说的两种访问控制,在结构上是散布在各个层级之间及整个结构与外部用户之间。
对用户进行访问控制是指:假设有 id,就会先检查哪些地址可以操作,这里 id 是指整个 DApp 的身份,和区块链身份不影响,因为区块链由于去中心化原因是没有身份的。但是开发的 DApp 里面可以定义一个用户名,这个在 DApp 里是可行的,和区块链没有关系。所以这里可以对这个地址进行检查,允许哪些地址进行操作。至于层之间的访问控制,和用户访问控制类似,用户 id 是Dapp 通行的身份,各层合约可以在 register 查询到也可以把这些合约地址作为整个结构中通行的身份。
具体在底层实现要用到两种特性,modifier 和 函数的可见性。通过这两种特性结合能够达到以上效果:某个合约只能某些合约调用,某些合约只能由某些 sender 调用,这样的控制。
modifier:
https://solidity.readthedocs.io/en/latest/contracts.html#function-modifiers
函数的可见性:
https://solidity.readthedocs.io/en/latest/contracts.html#visibility-and-getters
整个下来,代码的组织结构可能就如下图所示:
App: register
整个结构的分层,我们先从上往下我们开始讲。首先是 App 层的 register。regitser 是什么角色?他是注册中心,是一个 hub,我们期望它能够保存所有 DApp 智能合约的相关信息(地址等),这也是一个用户接入的入口。在这里,除 operator 后续升级和注册外,它相当于一个交互枢纽,可以进行部署、初始化、注册和升级。这里能够实现在 regiter 部署之后,整个 DApp 的智能合约部分也就部署上去了。我们看一下 register 在整个结构中的位置,是在 off-chain 与 后面操作、数据层之间。
为什么要这样做呢?不能直接逻辑层+数据层?这样一个中心化的东西会不会影响整个 DApp 的去中心化?
DApp 去中心化属性是依靠后面区块链实现的,有一个中心化的东西并不影响。它的一个优点是简单。通过一个智能合约能够管理所有模块,这个 register 是不变的,相当于一个不变的点,用来链接各个模块,保证稳定,相当于 DApp 在区块链上一直会有一个稳定的地址长期进行服务。如果,需要支持升级那么很多模块都可改变。然而,如果所有东西可以改变,则会变得很难维护,所以使用 register,能够随时通过这个东西进行查询和操作。还有一个优点是能够节约 Gas。只部署 register,然后就完成了整个 DApp 的部署。是怎么实现的?这里合约可以用 new 来生成其他智能合约。new 并不节约 Gas,节约的是交易相关的消耗。
Register 包含三类接口
第一类接口:初始化
也就是 Solidity 里面的 constructor,合约的构造函数。它的功能是在部署智能合约时,一次性执行然后销毁。所以初始化时,要存入什么?刚说 register 是一个稳定的东西,那就能把整个结构中,一些相对稳定的东西放在这里初始化。比如多个用户的操作层合约是固定的,而数据层的合约随着用户的注册注销而变化,那么就可以把操作层合约在这里初始化,随着 register 的部署由其进行创建。
第二类接口:注册
注册是 web 调用,由外部用户来使用的。在初始完之后,相当于整个 DApp 上线了,在用户使用时,可能就有些功能上的注册操作。比如 identity,用户需要注册一个用户名之类。在 register 部署之后,你能够完成初始化的其他操作,类比我们现在的应用运行之后提供的功能。
第三类接口:查询
这类接口的使用者分为两类:
Logic:Operator
接着往下看是操作层的合约。这里可以做一些模块化的东西,支持升级也是在这里做的,是因为简单。我们通常讲的支持升级包括两个方面,一个是函数或者接口的升级。另外一个是数据的升级或者说迁移。接口的升级比较容易,在区块链上数据升级比较困难,因为数据复制的操作很贵。存数据一字大概 2w gas。我们这里优先考虑 operator 的升级。
升级有两种方式,对应 evm 的智能合约里面进行交互两种指令,分别有两种升级方式
-
一个是 call,是消息调用的一种。调用 call 时,相当于把主动权交给另一个合约了,这个合约在一个新的 evm 执行之后返回一个结果给我。利用这个指令可以完成一个支持升级的方式,就是在 register 做一个类似 router 的东西,记录每一个操作类合约的版本号,然后用户就在访问操作类合约的时候选择版本进行下一次的调用,或者 register 帮你转。
-
第二个是用 delegate call,相对于 call,用 delegate 时主动权并没有交出去,整个智能合约代码还是在我现在的执行环境中执行,这是智能合约库使用的基本指令,很多库的实现都是基于 delegate call 的方式来做的。支持升级就是用一个代理类的合约,用户调用时帮你进行转发。这里会有一个副作用,必须把操作的数据留在 proxy 里面,这是 delegate 的属性。因为这个属性就需要支持升级的合约。有两个要求,一个是纯逻辑的,没有对状态的改变,第二个是没有在对数据留存在外面的要求,没有对数据进行分开的要求,所有版本的数据在 proxy 保存 。
我更推荐前者。后者把数据都存在 proxy 里面,前者是把数据也分开了,更模块化。我个人觉得是比较清晰的用法,这里用的也是这个。
DATA: data
关于数据,这方面的升级其实比较麻烦,会有一些问题。所以我们设计数据结构时尽量稳定一点,变动小一些,提前预留好以后要用的字段,避免以后的升级。要升级的话也有两种方法
-
一种方法是类似于插件的东西,旧的比如是 map 结构,是地址结构体,后面要多加一个字段。那么涉及旧数据怎么办?我可以定义一个插件类的合约,定义一个多余字段加一个指针指回原来的地方,相当于数据分开存。,但是保存一个指针指向旧数据并且能够找到他,能够做一些操作,这样的好处是不会变动数据,但是会增加操作的逻辑,比较复杂,而且不是所有的数据结构都能做的。
-
第二个是迁移,如果很有钱的话,可以直接拷贝过来,如果不在乎钱这是最简单的方式。
整个结构大概是这样。
对于升级的一点建议:升级时 copy 数据很贵,所以我们尽量避免这样的消耗,前期 gas 消耗也是注意的一个点。第二个是使用库来封装这些逻辑,就是说模块化。尽可能逻辑都能成库,可以找比较好的库来用。就是说很多模块交互需要用接口,让合约不依赖模块本身实现而依赖接口,这样保持接口不变的前提下就能升级合约。
关于讲师
冯开开
秘猿科技高级区块链工程师
Github:https://github.com/kaikai1024
秘猿科技 repo:https://github.com/cryptape
连接开发者与运营方的合作平台 CITAHub:https://www.citahub.com/
有任何技术问题可以在论坛讨论:https://talk.nervos.org
相关推荐
区块链技术是一种去中心化的分布式账本技术,它允许数据以安全、可靠、透明的方式在去中心化的网络环境中进行存储和传输。近年来,区块链技术因其去中心化、防篡改、可追溯和分布式存储等特性,在医疗信息系统中得到...
本文设计了一种基于区块链的物联网智能合约模型(BC-SC 模型),旨在解决物联网设备认证过程繁琐及其内部存储数据的隐私安全问题。该模型将区块链技术作为理论基础,采用分布式架构管理物联网设备运行,并利用 P2P ...
Arbitrum是一种新型的智能合约系统,其设计目标在于克服现有系统(如以太坊)所面临的可扩展性和隐私性限制。本系统的核心优势在于,它不仅支持智能合约的创建和执行,而且通过独特的机制设计,显著提升了系统的整体...
星云链Nebulas是一款基于区块链技术的去中心化应用平台,它支持智能合约的编译、部署和执行。在这篇文章中,我们将详细介绍星云链Nebulas智能合约的编译和部署过程。 智能合约的编译 在星云链Nebulas平台上,智能...
符号执行是一种强大的技术,它允许工具模拟合约的执行路径,寻找可能的异常行为。正式合约验证器则更进一步,它使用形式化方法证明合约的正确性,确保其满足预定义的逻辑和安全属性。 报告中提到了针对 Rust 语言...
以太坊智能合约编程主要涉及的是区块链技术中的一...7. 智能合约设计:理解如何构建具有特定业务逻辑的智能合约。 通过以上内容的学习,开发者可以掌握以太坊智能合约的开发和测试技巧,为构建去中心化应用奠定基础。
Vite还支持智能合约的开发和执行,并提供了一种称为Solidity++的新型智能合约语言,具有更高的灵活性和安全性。此外,Vite还提供了一套完整的工具和开发者支持,使得开发者可以更轻松地构建和部署去中心化应用程序。...
Solidity是专为以太坊区块链设计的一种高级编程语言,用于编写智能合约。它运行在以太坊虚拟机(EVM)上,支持创建复杂且安全的去中心化应用(DApps)。 一、Solidity语言特性与用途 Solidity作为面向对象的语言,...
智能合约投票系统Dapp是一种基于区块链技术的分布式应用程序(Dapp),它利用了以太坊网络上的智能合约功能,结合前端开发框架React和开发工具Truffle来构建。在这个项目中,我们将深入探讨智能合约、Truffle开发...
定义用于描述智能合约系统的体系结构,设计或代码中的安全性问题的通用语言。 用作培训和提高智能合约安全性分析工具性能的一种方式。创建一个新的SWC条目确保注册表中没有匹配的弱点。 理想情况下,还应与的社区...
`Proxy` 合约是智能合约设计中的一种模式,用于将函数调用委托给其他合约。在提供的代码中,`Proxy` 合约有一个`implementation()` 函数,该函数返回一个地址,表示实际处理业务逻辑的合约。当调用`Proxy` 合约的...
智能合约是一种运行在区块链上的自动执行合约条款的程序代码。它能够根据预设条件自动执行相应的操作,无需第三方干预。智能合约具备以下几个关键特点: - **去中心化**:不受任何单一实体控制,降低了被攻击的风险...
Solidity是专为以太坊区块链设计的一种高级编程语言,用于编写智能合约。在本案例中,我们看到Solidity的版本为^0.4.2,这意味着合约遵循该版本的语法和特性。开发者需要掌握Solidity的基础语法,如变量声明、函数...
Solidity作为一种专为以太坊设计的高级编程语言,它的正确性和安全性直接影响到智能合约的可靠性。 突变测试是一种有效的软件测试技术,旨在评估测试套件的质量和覆盖率。通过引入人为的错误,即突变运算符,来...
总结来说,星云链Nebulas的智能合约存储区LocalContractStorage提供了一种简单而强大的方式来管理和操作智能合约的状态。通过使用Key-Value存储模型,它支持快速的读写操作,并允许开发者使用熟悉的JavaScript语法...
"由goethereum支持的并行覆盖引导的突变Solidity智能合约模糊测试"是一种高级的安全测试方法,旨在发现智能合约中的潜在漏洞和错误。Goethereum,也被称为Geth,是以太坊官方的Go语言实现,提供了完整的以太坊节点...
符号执行和形式分析等技术虽然在智能合约的漏洞检测上有所应用,但尚未找到一种既能精确又可扩展的解决方案。 近期的研究工作开始转向利用深度学习,特别是图形神经网络(Graph Neural Networks, GNN)来提升智能...
智能合约是一种自我执行的合同,其条款和条件被编码在代码中,一旦满足预设条件,合同就会自动执行。由于其在金融、物联网等领域的广泛应用,智能合约的安全性至关重要。 1. **智能合约安全基础** 智能合约的安全...
Solidity是一种专为以太坊虚拟机(EVM)设计的高级编程语言,用于编写智能合约。智能合约是在区块链上运行的自动执行协议,它们在去中心化的网络中执行预定的规则和操作。以太坊官网提供了Solidity的官方文档,包括...
综上,这个项目涉及到了区块链技术的深度应用,结合现代Web开发框架,为学生社团提供了一种创新的、透明的治理模式。通过学习和理解这个项目,开发者不仅可以提升对区块链和智能合约的理解,还能掌握React前端开发的...