DOITAPP
DOIT数据智能产业媒体与服务平台
立即打开
DOITAPP
DOIT数据智能产业媒体与服务平台
立即打开

迅雷链来鑫系列课程二 | 智能合约的开发和安全指南

曾在百度参与建设分布式计算及网页搜索架构,后担任腾讯云高级工程师,主导去中心化负载均衡系统的大规模使用,以及分布式消息队列服务、信鸽移动推送、应用加固等多项技术研发。在分布式计算领域拥有丰富的经验。

2015 年加入迅雷,负责星域调度系统的技术研发,目前主导玩客云及区块链业务方案设计,建设共享计算的区块链底层平台。个人拥有分布式计算多项专利。

5 月 16 日,迅雷发布了星域云和迅雷链开放平台。关于迅雷链开放平台、如何基于迅雷链开发智能合约,以及开发合约的注意事项,来鑫有着最直接和专业的理解。

迅雷链的优势

目前,迅雷区块链已实现智能合约每秒百万次调用。在迅雷链基础上,已搭建起了区块链技术应用的开放平台,企业和个人开发者可以轻松将业务上链。作为区块链 3.0 的代表,迅雷链开放平台相比于其他主链具备很多优势:

1.技术优势:百万级并发处理能力、秒级确认快速可靠,适合企业 ToC 应用的大规模使用;

2.成本优势:成功接入的应用首年 gas 成本将全部由迅雷网心承担,从根本上解决开发者的成本之忧;

3.流量优势:共享 4 亿 + 用户基础,共享链克生态的百万量级活跃人群,塑造标杆产品对接迅雷核心推广资源;

4.投融资优势:联手知名投资机构,投资孵化。

迅雷链开放平台运行流程和接入

为了方便开发者调试,迅雷链提供沙盒环境,开发者在完成本地测试后,先在沙盒环境测试,在沙盒环境开发者可以申请给测试账号充值,调用接口部署合约,并执行合约进行逻辑测试。测试完成后使用沙盒环境中的合约地址到迅雷链开放平台提交发布申请。开发者在开放平台提交的合约代码必须和测试环境一致。

迅雷链官方审核开发者应用合规后,反馈开发者审核通过,开发者手动执行发布到链上,将区块链应用逻辑里使用的合约地址替换为合约在线上区块链发布后的地址。

关于合约的开发有哪些注意事项

迅雷链底层兼容 EVM,所以推荐开发者使用S    ty 语言开发智能合约。智能合约,实际上就是存储了代码的区块链账户,其他账户都可以通过给这个账户发送交易实现合约调用,以改变合约内存储的状态变量。下面是一个简单的商品买卖合约代码。

 

 

上面的合约,实现了一个简单的商品买卖合约,卖家部署合约并放入双倍商品价格的保证金,买家通过向合约发送交易调用 confirmPurchase 方法,并转入双倍商品价格的链克,来锁定交易,买卖双方在交易确认后,买家再次通过向合约发送交易调用 confirmReceived 方法,确认交易状态,合约向买家转入一倍商品价格的链克,向卖家转入剩余的全部链克,交易结束。

合约中限定了状态、调用者等 modifier,保证交易流程的正确。合约的状态变量记录了买卖双方的账户、商品的价格、交易的状态等信息,通过向合约发送交易调用合约内方法,来改变合约内部的状态变量值,最后这些交易在区块链同步出去,实现了合约代表的交易需求的去中心化。

合约通过 Solidity 语言编写,是当前的合约编写最流行的语言之一。这门语言受 C++,Python 和Javascript 语言的影响,设计的目的是能在以太坊虚拟机(EVM)上运行。Solidity 是静态类型语言,支持继承、库和复杂的用户定义类型等特性。详细 API 可参考

http://solidity.readthedocs.io/en/develop/index.html

通过truffle的智能合约的开发流程

truffle 是一套 Solidity 开发框架,集成了本地区块链环境,可快速编译、部署、调试合约代码。详情可参考 http://truffleframework.com/docs/

1.首先安装 truffle,依赖 nodejs,使用 npm -g truffle 命令安装。新建合约项目文件目录,使用truffle init命令初始化合约工程。

2.新建合约文件 contract/SimpleStorage.sol

3.添加 migrate 代码,新建文件 migrations/2_deploy_contract.js。后面会介绍这段代码的作用。

4.执行 truffle compile 编译合约,编译后的合约在 build 文件夹下。每个合约有一个对应的 json 文件,内含部署所需的 bytecode,abiCode

5.执行 truffle migrate 移植部署合约

6.执行 truffle develop,开启本地区块链环境,对已部署的合约进行测试

合约是通过 EVM(以太坊虚拟机) 来运行的,最终部署在区块链上,同步到区块链的交易中,生成合约账户。

truffle develop 命令可以生成一个本地区块链,并生成 10 个区块链账户,每个账户有 100 的余额。migrate 是 truffle 命令,可以通过执行 migrations 下的脚本,将合约部署到指定的区块链上。truffle.js可以配置部署和连接的区块链。上面第三步脚本实现的功能就是将 SimpleStorage 合约部署到区块链上,依赖的是 truffle 封装的功能。

truffle develop 生成的控制台环境,是本地区块链环境,内置 web3,并将 migration 里部署的合约实例化到上下文中,可以直接调用。

以太坊remix实现合约的部署和调用

除了使用 truffle 框架开发智能合约,还可以通过以太坊提供的 [remix](http://remix.ethereum.org/)快速实现合约的部署和调用。(由于某些网络原因,可能出现页面部分 js 不能完全加载)

remix 提供了合约的编译运行环境,并可以再控制台看到合约每条交易的详细信息,如输入输出参数,签名后的方法 data,交易 hash 等信息。支持调试。

1.使用 compile detail,可以看到合约编译详情。包括 bytecode,abi 和使用 web3.js 快速部署的方法。

2.使用run来create合约,控制台可查看创建合约的交易。

remix功能丰富,提供有本地区块链环境和线上etherum连接选择,支持单步调试,并可以看到详细的堆栈内容和assembly code。简单的合约直接通过remix部署和测试十分便捷。但是如果有依赖合约需要导入等方式实现的,不能很好的支持。也可以支持通过websocket连接本地项目。

智能合约有哪些安全注意事项?

由于智能合约是通过发送交易部署在区块链上的去中心化应用,这种性质就决定了合约一旦部署和调用操作成功,就不能回退。而合约中所保存状态和转移的资产,都具有重要的价值和意义,所以如果合约代码出现 bug,往往都会产生十分严重的后果。下面介绍一些合约开发过程中需要注意到的部分安全事项。

1.设置函数可视性和合理的 modifier 权限

有对外交互的函数通常设置为 public 或 external,内部函数设置为 private 或 internal。另外,一般使用相应的 modifier 限制函数操作的角色权限。这些都可以有效减少安全问题。

2. send 调用可能失败,需要检测 send 结果然后再确定是否进行下面的状态改变。

 

通常的操作逻辑可能会使,向某个账户发送一笔交易,然后改变合约内的某些数据状态。但是如果未对 send 函数调用的结果做检测,可能会出现 send 失败,但是后续状态依然被改变的结果,导致状态不一致。

下面例子展示(但并不是一个安全的合约例子):

如果msg.sender.send失败,但合约并未抛出异常继续执行,将导致 balances[msg.sender] 为 0,但msg.sender 并没有转入对应的 balance。所以需要对 send 的结果检测,并使用 throw 异常的方法,回滚已改变的状态值。

3.循环可能导致 gas 被消耗完而引起合约执行失败

循环变量可能依赖外部输入,如果循环的次数过多,将会导致 gas 消耗递增,直至超过 gasLimit,从而使得合约执行失败,回滚状态值,但是对应的 gas 却已经消耗不能退回。

4.调用堆栈深度限制

EVM 调用堆栈限制最深 1024 层,从资源交易占用角度看这是必须的。同时也意味着,如果嵌套调用的数量达到 1024,合约执行将会失败。攻击者可以递归调用一个合约 1023 次,然后再调用合约函数,造成失败一些 send 之类的失败,如上述第一条。

5.溢出漏洞

溢出,就是一个数字增加到它的最大值以上。Solidity 可以处理多达 256 位的数字(高达 2的256次方-1),所以递增 1 会得 0。最佳实践是通过 zeppelin-solidity 提供的 safeMath 来处理数值计算。

 

上面是 openzeppelin 实现的 safeMath 的乘法运算。如果乘法运算溢出,则 c/a 的结果不会等于b。以此保证运算的正确性。

6.可重入攻击 (DAO)

在使用 call 来发送 value 时,检测外部条件顺序的不合理或调用者设置 call 或 send 对应 fallback恶意转账,都可能导致状态不一致和金额的损失。

避免 DAO 攻击最佳实践就是使用 transfer 来实现转账,并使用 require 检测结果。

在合约 A 执行 to.call.value 时,如果 to 地址是合约地址,将会触发 to 的 fallback 函数来接收value。如果在 to 合约的 fallback 里递归调用了 A 合约的 withdraw 时,就是 DAO 攻击。

7. selfdestruct 的执行参数如果是合约地址,将不会执行合约的 fallback

如果一个合约 A 的 selfdestrcut(sender) 的 sender 是一个合约地址 B,B 合约将不会使用 fallback 来接收 A 的 selfdestruct 带来的转账。由此可能产生绕过合约逻辑的风险。

8.短地址攻击

短地址攻击是针对基于 ERC20 类型的 token transfer 时的问题。在调用 transfer (address addr, uint amount) 时,实际发送交易的是 abi 编码后的16进制代码,其中每个参数长度是固定的,如果长度不足会自动补 0。漏洞即源于此,如果 addr 的最后是以 0 结尾的,而攻击者少输入最后的 0,amount 编码高位补 0,导致 amount 编译值比实际输入值大。从而实现转移超出实际应该 transfer 的数量的 token。实践中,需要在 transfer 方法检查长度来限制此类问题。

合约的安全实践需要持续的关注和更新,并做好合约的审计和升级,必要情况下可设置一些紧急停止或转移合约功能的方法,以备不时之需。

文/来鑫

未经允许不得转载:DOIT » 迅雷链来鑫系列课程二 | 智能合约的开发和安全指南