笔记只是促进理解和记忆的手段,想要有所成就,还需要动手去做。
主题:智能合约的开发和安全指南
智能是自我执行合约,在智能合约中,合约条款由代码规定。基本上,这意味着可以用计算机程序编写具有法律效力的合约,而且这个合约可以自动执行。至少在1996年Nick Szabo就提出了这一感念,尽管这个感念已经出现了一段时间了,但是知道图灵完成Ethereum区块链,智能合约的使用才开始普及。但是执行用代码书写的合约并且在一个不可改变的公开的区块链中存储信息造成一定的风险和问题。这也是我们接下来要讨论的问题。
迅雷从2015年开始对分布式计算和区块链领域的布局。迅雷链的诞生,代表着迅雷想着技术平台化的转变---过去迅雷拥有的下载/上载的能力,现在组成了一个新品的产品链路,迅雷链上拥有的巨大双利,以及广阔的节点。迅雷链一方面具备强大的并发处理能力,具备划分时代的硬实力。另一方面,高性能区块链技术方案,阻力开发者降低成本,快速部署智能合约,更加便捷的开发区块链饮用。特别是载传统实体经济领域,迅雷超级区块链具备了超强赋能能力。
在迅雷链基础上,我们搭建了区块链技术应用开放平台,企业和个人开发者可以轻松将业务上链作为国球区块链3.0的代表,迅雷链开放平台比于其它主链具备很多优势。
1.技术优势:百万级别并发处理能力、秒级确认快速可靠,适合企业ToC应用的大规模使用;
2.成本优势:3亿链克全部投入开发者扶持计划,成功接入的应用首年gas成本将全部由迅雷网中心承担,从根本上解决开发者的成本之忧;
3.流量优势:共享4亿+用户基础,共享链克生态的百万量级活跃人群,塑造标杆产品对接迅雷核心推广资源;
4.投融资优势:联手知名投资机构,投资孵化。
一。下面介绍迅雷链开放平台的运行流程以及开发者如何接入
1. 为了方便开发者调试,我们提供沙盒环境,开发者在完成本地测试后,先在沙盒环境测试,在沙盒环境开发者可以申请给测试账号充值,调用接口部署合约,并执行合约进行逻辑测试。
2. 测试完成后使⽤沙盒环境中的合约地址到迅雷链开放平台提交发布申请。开发者在开放平台提交的合约代码必须和测试环境⼀致
3. 迅雷链官方审核开发者应用合规后,反馈开发者审核通过,开发者手动执行发布到链上,将区块链应用逻辑里使用的合约地址替换为合约在线上区块链发布后的地址。
二。合约开发注意事项
迅雷链底层兼容EVM,所以推荐开发者使⽤Solidity语⾔开发智能合约。智能合约,实际上就是存储了代码的区块链账户,其他账户都可以通过给这个账户发送交易实现合约调⽤,以改变合约内存储的状态变量。
下面是一个简单的商品买卖合约代码
上面的合约,实现了一个简单的商品买卖合约,卖家部署合约并放入双倍商品价格的保证金,买家通过向合约发送交易调用confirmPurchase方法,并转入双倍商品价格的链克,来锁定交易。
买卖双方在交易确认后,买家再次通过向合约发送交易调用confirmReceived方法,确认交易状态,合约向买家转入一倍商品价格的链克,向卖家转入剩余的全部链克,交易结束。
合约中限定了状态、调用者等modifier,保证交易流程的正确。合约的状态变量记录了买卖双方的账户、商品的价格、交易的状态等信息,通过向合约发送交易调用合约内方法,来改变合约内部的状态变量值,最后这些交易在区块链同步出去,实现了合约代表的交易需求的去中心化。
Solidity是静态类型语言,支持继承、库和复杂的用户定义类型等特性。详细API可参考http://solidity.readthedocs.io/en/develop/index.html
合约通过Solidity语言编写,是当前的合约编写最流行的语言之一。这门语言受C++,Python和Javascript语言的影响,设计的目的是能在以太坊虚拟机(EVM)上运行。
三。接下来将通过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里部署的合约实例化到上下文中,可以直接调用。
除了使用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结果然后再确定是否进行下面的状态改变。
<address>.send(uint256) returns (bool)
通常的操作逻辑可能会使,向某个账户发送一笔交易,然后改变合约内的某些数据状态。但是如果未对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²⁵⁶-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方法检查长度来限制此类问题。
合约的安全实践需要持续的关注和更新,并做好合约的审计和升级,必要情况下可设置一些紧急停止或转移合约功能的方法,以备不时之需。