共计 9154 个字符,预计需要花费 23 分钟才能阅读完成。
编者荐语:
Solidity 应用起来不如其余语言那般丝滑顺畅?安全事故难以避免?社区迎来实用于 FISCO BCOS 的 Solidity 智能合约库,轻松破解合约的各种小难题,让合约开发更加疾速高效、省时省力。目前,该合约库已全面开源回馈社区,欢送参加共建。
以下文章来源于微众银行区块链,作者:微众区块链
区块链技术在经验了十余年的倒退后,渐呈“燎原之势”,一直在各行业落地生根。但同时,从技术的角度看,区块链利用开发依然有着较高的门槛,存在不少痛点。为了晋升利用开发各环节的用户体验,微众银行将自主研发的区块链利用开发组件 WeBankBlockchain-SmartDev 全面开源,多维度提速区块链利用开发效率。开源布告详见《区块链利用开发组件: 助力低代码开发》。
WeBankBlockchain-SmartDev 所蕴含的智能合约库组件,涵盖了从根底类型到下层业务的常见 Solidity 库代码,用户可依据理论须要进行参考、复用。该智能合约库曾经集成到国家信息中心顶层设计的区块链服务网络 BSN、微众银行自主研发的区块链中间件平台 WeBASE 中,并在供应链金融、存证、农牧溯源等多个业务场景中广泛应用。咱们期待区块链技术爱好者和开源社区的搭档们,一起参加共建,协力推动 Solidity 智能合约库向更成熟的技术、更欠缺的生态上倒退。
智能合约库简介
作为一门实现了图灵齐备的智能合约编程语言,Solidity 编程语言的开发、设计、迭代、演变的逻辑齐全基于区块链,并在区块链畛域具备宽泛的影响力和详尽的文档,被泛滥区块链底层平台所反对,其中就包含 FISCO BCOS。
然而,Solidity 编程语言也存在若干挑战。首先,受区块链低廉的资源限度,Solidity 舍去了诸多在其余语言中常见的个性,例如高级的语法等。其次,风行的 Solidity 智能合约库多为私有链所开发,与 FISCO BCOS 存在兼容性的问题。最初,智能合约编程的安全性要求高,且较难对合约进行降级,一旦存在安全漏洞,结果不堪设想。
为了解决上述问题,WeBankBlockchain-SmartDev-Contract 智能合约库应运而生,蕴含了根底类型、数据结构、通用性能、下层业务等智能合约库。用户可依据理论需要进行参考、复用。智能合约库的设计初衷是提供场景化、通用化、可插拔的智能合约代码,从而最大水平地节约开发智能合约的工夫,扭转智能合约工具库匮乏的场面。
SmartDev-Contract 智能合约库是一个“麻雀虽小,五脏俱全”的智能合约的工具类库,通过 Solidity 的 library 封装,旨在帮忙 Solidity 开发者晋升开发体验,防止反复造轮子,让编写 Solidity 语言也能够如编写 Python 语言那样“丝滑顺畅”。
SmartDev-Contract 智能合约库的每个合约文件都来自于微众银行区块链工程师的粗疏打磨,来自于理论应用场景的“聚沙成塔”,笼罩了业务场景开发中的各种“犄角旮旯”,是开发智能合约的“10 倍工程师”的不二法门。
从性能上来看,SmartDev-Contract 智能合约库涵盖了从根底类型到下层业务的常见代码,用户可依据理论须要进行参考、复用。具体如下:
痛点及解决形式
以在 Solidity 语言中将 address 类型转为 string 为例。
过来:
关上搜索引擎或 github-> 搜寻关键字“Solidity address convert to string”-> 找到相干的搜寻后果 -> 拷贝相干的代码 -> 粘贴到本人的智能合约代码中。
如果无奈搜寻到相干的代码,开发者必须从新造轮子,耗时耗力的同时,还可能引入新的危险。
当初:
间接下载智能合约代码库 -> 解压 -> 找到相干的库合约 -> 引入代码 -> 调用相干函数。智能合约代码库地址:
https://github.com/WeBankBloc…
pragma solidity ^0.4.25;
import "./LibAddress.sol"
contract Demo {
...
address addr = 0xE0f5206BBD039e7b0592d8918820024e2a7437b9;
bytes memory bs = LibAddress.addressToBytes(addr);
}
开发者引入或拷贝网络上未知起源的代码可能呈现重大的 bug。同样的,本人从新编写的代码可能因为不足测试或实际测验,更易呈现危险。智能合约库提供了不便、成熟、平安、低成本的解决方案。
痛点一:计算可能溢出
在智能合约的开发中,数值计算问题不可避免。然而,Solidity 内置的运算机制不够平安,因计算问题导致的智能合约安全事故不足为奇。
SmartDev-Contract 智能合约库提供了平安计算的代码类库。以 uint256 数据类型为例,LibSafeMathForUint256Utils 提供了 Uint256 类型的相干计算操作,且保证数据的正确性和安全性,包含加法、减法、乘法、除法、取模、乘方、最大值、最小值和平均数等操作。其余的数值类型能够自行参考实现。
1、加减乘除运算
function f() public view {
uint256 a = 25;
uint256 b = 20;
// a + b
uint256 c = LibSafeMathForUint256Utils.add(a,b);
// a - b
uint256 d = LibSafeMathForUint256Utils.sub(a,b);
// a * b
uint256 e = LibSafeMathForUint256Utils.mul(a,b);
// a/b
uint256 f = LibSafeMathForUint256Utils.div(a,b);
}
2、取模运算、乘方运算
function f() public view {
uint256 a = 25;
uint256 b = 20;
// a % b
uint256 c = LibSafeMathForUint256Utils.mod(a,b);
// a ^ b
uint256 d = LibSafeMathForUint256Utils.power(a,b);
}
3、最大值、最小值、平均数运算
function f() public view {
uint256 a = 25;
uint256 b = 20;
// max(a, b)
uint256 c= LibSafeMathForUint256Utils.max(a,b);
// min(a, b)
uint256 d = LibSafeMathForUint256Utils.min(a,b);
// average(a, b)
uint256 e = LibSafeMathForUint256Utils.average(a,b);
}
痛点二:转换不够便捷
数值转换工具
根底数据类型转换是编程语言的刚需。LibConverter 提供各类 Solidity 数据根本类型的转换,开发者能够依据此工具扩大为其余的数值转换类型和函数。
1、数值类型向下转换,例如 uint256 转换为 uint8。
function f() public view{
uint256 a = 25;
uint8 b = LibConverter.toUint8(a);
}
2、数值类型转 bytes
function f() public view{
uint256 a = 25;
bytes memory b = LibConverter.uintToBytes(a);
}
3、bytes 转数值类型
function f() public view{
bytes memory a = "25";
int b = LibConverter.bytesToInt(a);
}
address 转换工具
address 类型是 Solidity 特有的数据类型之一。在日常的程序运行逻辑中,经常会波及到 address 与 bytes 和 string 类型的互转。LibAddress 实现了上述的转换性能。
address 转 bytes
address addr = 0xE0f5206BBD039e7b0592d8918820024e2a7437b9;
bytes memory bs = LibAddress.addressToBytes(addr);
bytes 转 address
bytes memory bs = newbytes(20);
address addr = LibAddress.bytesToAddress(bs);
address 转 string
address addr = 0xE0f5206BBD039e7b0592d8918820024e2a7437b9;
string memory addrStr = LibAddress.addressToString(addr);
string 转 address
string memory str="0xE0f5206BBD039e7b0592d8918820024e2a7437b9";
address addr = LibAddress.stringToAddress(str);
痛点三:数组操作不够丰盛
在 Solidity 中原生反对的数组类型,不反对排序、查找、比拟、移除、增加、翻转、合并、去重等泛滥罕用的性能。
SmartDev-Contract 智能合约库基于动静数组的构造封装了“LibArrayForUint256Utils”的常用工具函数实现。开发者也可依据本身须要的数据结构,自行封装相干的工具类。
1、增加不反复的元素
uint[] private array;
function f() public view {array=new uint[](0);
// array add element 2
LibArrayForUint256Utils.addValue(array,2);
// array: {2}
}
2、合并两个数组
uint[] private array1;
uint[] private array1;
function f() public view {array1=new uint[](2);
array2=new uint[](2);
LibArrayForUint256Utils.extend(array1,array2);
// array1 length 4
}
3、对数组去重
uint[] private array;
function f() public view {array=new uint[](2);
array[0]=2;
array[1]=2;
LibArrayForUint256Utils.distinct(array);
// array: {2}
}
4、对数组升序排序
uint[] private array;
function f() public view {array=new uint[](3);
array[0]=3;
array[1]=2;
array[2]=1;
LibArrayForUint256Utils.qsort(array);
// array: {1,2,3}
}
5、二分查找
基于已排序的数组,反对二分查找,晋升查找的效率。
uint[] private array;
function f() public view {array=new uint[](3);
array[0]=3;
array[1]=2;
array[2]=1;
uint256 key=3;
LibArrayForUint256Utils.binarySearch(array,key);
// array: {true, 1}
}
6、删除元素
uint[] private array;
function f() public view {array=new uint[](3);
array[0]=3;
array[1]=2;
array[2]=1;
LibArrayForUint256Utils.removeByValue(array,2);
// array: {3, 1}
}
痛点四:不提供字符串内置操作
对字符串的操作是开发中较为常见的操作,例如获取字符串长度、大小写转换等。在其余开发语言中,通常会提供一些内置的字符串解决类库。但 Solidity 自身没有提供字符串内置操作,因而,这部分需要可通过应用 SmartDev-Contract 智能合约库来满足。
在 SmartDev-Contract 智能合约库中,对于字符串,咱们提供了丰盛的性能,这里列举三个比拟常见的函数。
1、获取字符串长度
上面的示例中,别离示意了获取字符串长度、字符串字节数:
pragma solidity ^0.4.25;
import "./LibString.sol";
contract Test {function f() public{
string memory str = "你好";
uint256 lenOfChars = LibString.lenOfChars(str);
uint256 lenOfBytes = LibString.lenOfBytes(str);
require(lenOfChars == 2);
require(lenOfBytes == 6);
}
}
2、大小写转换
上面示例中,将大写转换为小写:
pragma solidity ^0.4.25;
import "./LibString.sol";
contract Test {function f() public view returns(string memory) {string memory c = LibString.toUppercase("abcd");// Expected to be ABCD
return c;
}
}
3、相等比拟
pragma solidity ^0.4.25;
import "./LibString.sol";
contract Test {function f() public view {bool r = LibString.equal("abcd","abcd");//Expected to be true
require(r);
}
}
痛点五:高级数据结构不齐备
作为一门面向区块链的语言,Solidity 为了节俭资源,在数据结构层面砍掉了许多个性,这使得和惯例语言相比,其在应用上存在较大差别。一方面,Solidity 外部仅提供了数组、mapping 等数据结构,如果存在其余需要,需自助实现;另一方面,对于 mapping,其外部的键仅保留了哈希值,无奈获取键的原值。
综上所述,咱们在 SmartDev-Contract 智能合约库中提供了对数据结构的加强反对,以资参考、应用。
Mapping 映射
在上面这个例子中,定义了一个 Map,而后向外面寄存了三个键值对。再通过迭代的形式将 key 取出,寄存在事件里。
pragma solidity ^0.4.25;
import "./LibBytesMap.sol";
contract Test {
using LibBytesMap for LibBytesMap.Map;
LibBytesMap.Map private map;
event Log(bytes key, uint256 index);
function f() public {
string memory k1 = "k1";
string memory k2 = "k2";
string memory k3 = "k3";
string memory v1 = "v1";
string memory v2 = "v2";
string memory v3 = "v3";
map.put(bytes(k1),bytes(v1));
map.put(bytes(k2),bytes(v2));
map.put(bytes(k3),bytes(v3));
// 开始迭代
uint256 i = map.iterate_start();
while(map.can_iterate(i)){emit Log(map.getKeyByIndex(i), i);
i = map.iterate_next(i);
}
}
}
address set 汇合
作为少数高级编程语言标配的数据结构,set 是一种汇合的数据结构,其中每个独特属性的元素都是惟一的。
SmartDev-Contract 智能合约库依靠动静数组和 mapping,实现了一个根底的 set 汇合。此外,因为 Solidity 不反对泛型机制,开发者能够参考此工具,实现其余元素的 set 汇合。
pragma solidity ^0.4.25;
import "./LibAddressSet.sol";
contract Test {
using LibAddressSet for LibAddressSet.AddressSet;
LibAddressSet.AddressSet private addressSet;
event Log(uint256 size);
function testAddress() public {
// 增加元素;addressSet.add(address(1));
// {1}
// 查问 set 容器数量
uint256 size = addressSet.getSize();
require(size == 1);
// 获取指定 index 的元素
address addr = addressSet.get(0);
require(addr == address(1));
// 返回 set 中所有的元素
addressSet.getAll();
// {0x1}
// 判断元素是否存在
bool contains = addressSet.contains(address(1));
require(contains== true);
// 删除元素
addressSet.remove(address(1));
}
}
除了 LibBytesMap 和 LibAddressSet 之外,SmartDev-Contract 智能合约库还蕴含了堆、栈、单向队列、双向队列、双向链表等实用的数据结构。
业务场景的合约模板
针对下层的业务场景,咱们抉择联盟链落地中最常见、典型的存证和积分业务场景,提供智能合约代码实例。开发者能够基于本身的理论业务场景批改智能合约代码,也能够参考场景中的局部代码,进行扩大和裁剪。
场景一:存证场景
电子数据存证是记录“用户身份验证 - 数据创立 - 存储 - 传输”全过程的形式,利用一系列平安技术全方位确保电子数据的真实性、完整性、安全性,在司法上具备残缺的法律效力。
应用区块链智能合约进行数据存在,具备以下劣势:
- 区块链技术欠缺的防篡改机制:应用区块链技术顾全证据,进一步增强了证据不可篡改性。
- 证据效劳失去机构认可:司法机构作为链上节点,对链数据参加认可和签名,预先可从链上确认数据的实在有效性。
- 服务继续无效:数据被多方共识上链后,即便有局部共识方退出也不会造成数据的失落或生效。
简要业务流程
- 在存证场景中能够形象出三类典型用户:存证方、审核方和取证方。存证方提交须要存证的申请。
- 审核方基于内容,对存证数据进行审核和签名确认。理论业务场景中,审核方可能会波及投票和多方审核的多签过程。
- 当存证上链后,取证方可随时查问存证者地址、工夫戳和审核详情等相干信息进行核验。
合约概要设计
首先,代码设计了逻辑和数据层拆散。因为 Solidity 智能合约语言没有独立数据层,为便于合约后续扩大、降级,须要将逻辑和数据层拆散,体现在下图中就是将数据层和管制层辨别开。
其次,根据合约繁多职责原理,代码中引入了权限层。在一条联盟链上所有节点能够自在拜访链上数据,智能合约提供了一种润饰器机制,可管制合约给指定受权用户拜访。
具体的构造如下:
- Authentication:权限合约,用于提供根底的权限管制性能。
- EvidenceRepository:存证数据仓库,它继承了权限合约,所有存证数据都被保留到数据合约里。这样能够起到对立存储、对立治理的成果。
- RequestRepository:申请数据仓库,存储了存证数据和投票申请信息等。存证方开始提交存证数据并不会间接被写入存证仓库中,而是通过审核方签名实现后才会真正提交,审核方能够为多方。
- EvidenceController:控制器,引入了两个数据仓库合约,能够实现所有用户接口的交互。蕴含了创立存证申请,审核人依据申请进行投票的性能。
场景二:积分场景
区块链积分场景是指多个独立对等的零售商组成联盟,利用公众联盟链为消费者提供真正意义上的全渠道综合生产体验。
在积分场景中引入区块链,具备以下劣势:
- 区块链技术能够减少品牌曝光度:多个机构组成积分联盟,积分可无效通兑,实现客户资源引流,晋升营销成果。
- 保障积分安全性:所有积分的生成和流转过程保留到链上,避免商户篡改和抵赖。
- 晋升用户体验:不同商户和用户之间实现积分流转、互通,更加便当。
简要业务流程
基于区块链技术,多个商家组成积分联盟,实现积分通存通兑、客户资源互相引流等。管理者部署和治理合约。商家有发行积分、拉入其余商家、撤销发行者身份的权限。消费者有开户、销户、生产积分和积分转账的权限。
基于区块链技术,多个商家组成积分联盟,实现积分通存通兑、客户资源互相引流等。管理者部署和治理合约。商家有发行积分、拉入其余商家、撤销发行者身份的权限。消费者有开户、销户、生产积分和积分转账的权限。
合约概要设计
在存证合约中,咱们引入了数据和逻辑拆散的思维;在积分合约中,咱们引入治理、数据和逻辑拆散的思维。
引入了治理合约后,就实现了相似管制反转的成果,管制合约和数据合约都由治理合约来创立;同时,治理合约还能够随时设置数据合约中管制合约的地址。这样,管制合约就能够随时实现平滑的业务逻辑降级;将治理合约分离出来,还有利于链上权限治理。
- Admin:治理并生成合约,管制拜访 Data 合约的地址
- Controller:合约层对外裸露服务的控制器
- Data:存储业务合约相干的数据
- BasicAuth、IssuerRole:权限、角色的工具合约
- LibRoles、LibSafeMath:权限 mapping,数值计算的库
即刻体验
上述优化及性能所波及的最新代码和技术文档已同步更新,欢送体验和 star 反对。如需征询技术问题,欢送关注本公众号,对话框回复 【小助手】 进技术交换群。
文档地址:
https://smartdev-doc.readthed…
GitHub 代码库地址:
https://github.com/WeBankBloc…
gitee 代码库地址:
https://gitee.com/WeBankBlock…
欢送参加 WeBankBlockchain 的社区建设:
- 如我的项目对您有帮忙,欢送点亮咱们的小星星(点击我的项目左上方 Star 按钮)。
- 欢送提交代码(Pull requests)。
- 发问和提交 BUG。
如果发现代码存在安全漏洞,可通过以下地址上报:
https://security.webank.com/
FISCO BCOS 的代码齐全开源且收费
下载地址↓↓↓
https://github.com/FISCO-BCOS…