发布时间:2020-11-09 来源:本站
作者:毛嘉宇|FISCO BCOS 核心开发者
场景1:区块链+存证的权限合约编写
电子数据存证是记录“用户身份验证-数据创建-存储-传输”全过程的方式,应用一系列安全技术全方位确保电子数据的真实性、完整性、安全性,在司法上具备完整的法律效力。
服务持续有效:数据被多方共识上链后,即使有部分共识方退出也不会造成数据的丢失或失效。
存证场景简要业务流程
当存证上链后,取证方可随时查询存证者地址、时间戳和审核详情等相关信息进行核验。
存证场景权限合约实例讲解
我们以存证场景中权限合约进行讲解。
存证合约概要设计
首先将逻辑和数据层分离。因为Solidity智能合约语言没有独立数据层,为便于合约后续扩展、升级,需要将逻辑和数据层分离,体现在下图里就是将数据层和控制层区分开。
其次引入权限层。在一条联盟链上所有节点可以自由访问链上数据,智能合约提供了一种修饰器机制,可控制合约给指定授权用户访问,依据合约单一职责原理,将这一层抽象出来。
同时,我们需要控制数据层的权限,防止将写入数据层的接口权限开放给所有人,因此需要依赖并引入权限合约。
权限合约实例讲解
权限合约比较简单,不需要依赖其他合约,在很多合约开发中存在需求,可供复用。
acl:acl的变量是一个address到bool类型的mapping,我们看下allow和deny函数,allow将传入参数的地址所映射bool值设置为true,deny设置为false,设置完后可以通过auth修饰器来判断合约访问者权限。
存证数据
下图展示的是证据数据层,存证数据合约的代码。
EvidenceRepository是存证数据仓库,它继承了权限合约,权限合约里的方法和修饰器可在存证仓库的合约被使用。
定义的函数中只确定了最核心的setData和getData函数。请注意,该合约本身继承了Authentication合约,所以在setData里能够使用auth的修饰器,控制只有被授权用户才能访问,防止智能合约被部署到链上以后遭到恶意攻击或调用。getData函数根据传入的哈希把存入数据整体查询出来并返回。
可以看出,所有存证数据都被保存到数据合约里。这样可以起到统一存储、统一管理的效果。当然,这不一定是最优方案。
在业务场景中,如果合约拥有海量存证数据,则可能成为性能瓶颈,采用分拆设计方案会更加合理。
请求数据
存证方开始提交存证数据并不会直接被写入到存证仓库中,而是经过审核方签名完成后才会真正提交,审核方可以为多方。
理解合约用途后,我们仔细看这个合约。
最后来看两个核心的函数:创建请求和投票审核。两个函数内都有auth修饰符控制权限。创建请求时,函数内部会使用require语句检查请求是否已经存在;投票时,函数内部会使用require语句检查审核者是否已经投票,请求本身是否存在,审核者是否合法。如果检查通过,审核投票票数加一,标记审核者已签名。
控制器
控制器引入了两个数据仓库合约,我们只需调用controller就可以完成所有用户接口的交互;它的构造函数参数变量包含了请求合约构造所需的参数:审核者列表及投票阈值,这个构造函数会自动构造和创建合约。
controller定义了两个方法,一个是创建存证请求,另一个是审核人根据请求进行投票。
创建请求函数较为简单,会直接调用请求数据仓库合约里的创建请求函数。
处理投票函数相对复杂。在验证hash数据非空后,会调用审核接口,如果审核成功,会触发检查当前请求审核通过数是否超过阈值,一旦超过,就自动保存到存证数据合约,同时,删除该请求。
此外,这个合约里还定义了三个event事件,有以下作用:
提供一个过滤器,支持参数检索和过滤。
例如,createSaveRequest日志记录了hash和调用地址。如果配合SDK,我们可以实现对这个特定事件监听,并自动触发自定义回调函数。
存证合约示例小结
以上就是一个完整的存证场景权限合约demo。为了便于理解,我们并没有把例子设计得面面俱到,希望大家能更好地从中理解到demo的设计思想:
存证数据hash上链。
场景2:区块链+积分合约实例讲解
下面介绍另一个智能合约典型应用场景——积分场景。
提升用户体验:不同商户和用户之间实现积分流转、互通,更加便利。
图:典型积分业务场景示例
一种思路是基于区块链技术,多个商家组成积分联盟,实现积分通存通兑、客户资源相互引流等。我们抽象了一个管理者,管理者部署和管理合约,商家有发行积分、拉入其他商家、撤销发行者身份的权限;消费者有开户、销户、消费积分和积分转账的权限。
积分场景合约实例讲解
先来做积分合约的概要设计。在存证合约中,我们引入了数据和逻辑分离的思想;在积分合约中,我们将引入管理、数据和逻辑分离的思想。
为什么要增加一层管理合约呢?原有的两层结构中,控制合约会自动创建数据合约,而数据层合约中写死了属主是控制合约。
引入了管理合约后,就实现了类似控制反转的效果,控制合约和数据合约都由管理合约来创建;同时,管理合约还可以随时设置数据合约中控制合约的地址。这样,控制合约就可以随时实现平滑地业务逻辑升级;将管理合约分离出来,还有利于链上权限治理。
此外,我们还将常用的权限、角色功能抽象为合约,并抽象了权限mapping和数据计算的库。
下面我们将看下积分合约具体的代码实现。
合约库——安全计算
安全计算在Solidity中非常重要。涉及到数值计算部分,可优先考虑采用成熟开源的库。分享一个小技巧,由于链上资源非常宝贵,建议大家使用库的时候可以裁剪掉冗余的代码,节省资源。
安全计算的库,会在执行后重新检查数值,避免出现溢出,规避攻击。
库——角色管理
角色管理的库提供了创建角色、删除角色、查询角色的功能。这里有个基础的mapping bearer,是address到bool的映射,在mapping中维护role身份。
基础权限合约
在BasicAuth的基础权限合约中,我们提供了对属主的判断。
发行者合约
发行者合约依赖上面LibRole的合约。为了简化规则,易于理解,我们这样定义:发行者允许添加新的发行者,也可以撤销自己发行者身份。有了发行者角色后,我们就可以发行积分了。
积分数据合约
现在进入到积分合约主体——admin-controller-data三层架构。
首先介绍积分数据合约。它会将所有用户积分,以及角色信息都存到积分数据合约里。
setBalance方法,设置某个账户的余额必须通过onlyLatestVersion检查,只有有权限的owner才能调用这个数据合约。
管理合约
管理合约作用是创建所有的合约。constructor更新在数据合约中所持有的版本号,一旦controller需要升级,只需调用下这个方法即可。
控制合约
最后介绍控制合约。由于controller代码较长,这里只展示最典型的两个函数。
balance查余额调的就是数据查余额的接口。积分的消费通过transfer实现,其中会有很多修饰器来检查账户是否已经注册,是否有效等,此外采用了智能合约事件来输出和打印日志。
积分合约示例小结
以合约为单位,对职责进行抽象,尽可能实现合约职责单一。
如何写出高质量设计说明文档
文档编写之『术』
使用说明:实际使用场景、上手指南和使用手册等。
文档编写之『道』
要点:以解释作为基本导向,不要预设别人能够理解所有业务和技术术语。
本文主要分享了存证和积分两个典型应用场景合约设计思路和实例代码解析,为大家总结了相关的开发技巧和文档写作技巧。
在智能合约开发过程中,开发者需根据实际业务需求选择适用的技巧与方案。正所谓『兵无常势,水无常形』,没有最优的设计,只有最合适的设计。