以太坊自2013年V神提出后,被无数人赋予美好的愿景,甚至被称为区块链2.0,其代币发行量更是达到了全球第二,仅次于比特币,而其带来的智能合约概念颠覆了人们对区块链的理解,让区块链不仅仅是个账本,更像一个操作系统,赋予了每个节点“智能”。经过差不多半年来断断续续的学习、理解和沉淀,笔者今天想揭开以太坊DApp神秘的面纱,看看以太坊是猴还是猿。
DApp(decentralized applications),中文名是去中心化应用,由一系列智能合约组成,而智能合约可简单理解为代码和数据的集合,运行于以太坊各个节点上,更详细的介绍可参考笔者第一篇文章https://cloud.tencent.com/developer/article/1158839 ,这里先简单介绍一下相关名词。
DApp的开发和运行方式现在可以说是五花八门,笔者整理出以下几种供大家参考使用。
Remix | TestRPC | Private Ethereum | Public TestNet | Public Ethereum | ||
---|---|---|---|---|---|---|
Access Method | Remix | Node console and web3 | Node console and web3 | Wallet and web3 | Wallet and web3 | |
Ethereum Blockchain | Javascript VM inside Remix | TestRPC (memory) | Private Ethereum Blockchain (disk) | Public testnet Ethereum Blockchain | Public Ethereum Blockchain |
一个DApp被调用之前需要先部署到以太坊上,不管是私链,公链还是联盟链。故本章节分为两部分,DApp部署原理和调用原理。
一个DApp由多个智能合约组成,部署一个DApp也就是同时部署多个智能合约,这里讲述一下部署一个智能合约的流程,如下图所示。
笔者这里推荐使用Remix进行部署,因为Remix不仅可以连接浏览器内嵌的以太坊VM,还可以和MetaMask联动,使用MetaMask当前所连的以太坊网络,而且Remix还可以调试部署好的合约,十分方便。在Remix上部署十分简单,选择部署的以太坊网络和填好合约初始化参数后,点击create按钮即可。如果我们选的是MetaMask当前的以太坊网络,则会跳转交易界面,因为部署合约本质上也是一笔交易,我们需要付交易手续费,如下图所示。
部署后的合约其实外部还不能调用,还需要我们上传源代码进行验证,不然别人不可能在不清楚源码情况下向合约发起交易,向你转账,如下所示。
部署好的合约我们就可以调用了,根据调用方式的不同,本文分为前端调用和后端调用。
前端调用相对来说简单一点,因为有MetaMask这个强大的插件,我们不必操心以太坊数据源,直接调用web3.currentProvider
即可,下图展示了前端调用合约的一般流程,由于前端连接的Web3 Provider是与特定的以太坊节点相连,前端不需要管方法的签名,只需无脑调用合约中的方法即可。
至于后台调用就麻烦一点了,由于后台没有MetaMask这么方便的工具可调用,因此要是调用公链上的智能合约,只能使用特定账户的私钥签名方法后,并且以该账户的身份调用合约,流程如下图所示。
以太坊Web3.js提供调用合约的方法一共有四种:
//合约声明
contract test {
function multiply(uint a) returns(uint d) {
return a * 7;
}
}
//合约调用
var Multiply7 = eth.contract(contract.info.abiDefinition);
var myMultiply7 = Multiply7.at(address);
myMultiply7.multiply.call(3)
wheelOfFortune.makeBet.sendTransaction(num - 1, betCount, tips, { from: web3.eth.accounts[0], value: betUnit \* betCount + tips }).then(function (result) {
if (result.logs.length > 0) {
var eventobj = result.logs[0].args;
$.ajax({
url: '/Wheel/makeBet/' + eventobj.pieceIdx + '/' + betCount + '/' + tips + '/' + web3.eth.accounts[0],
success: function (data) {
var nextRound = JSON.parse(data);
$("#currentTotal").text(nextRound.betPool.toLocaleString());
playersNumberOfPiece = nextRound.playersNumberOfPiece;
alert('下注成功');
}
});
}
});
constant
修饰时,直接调用会等同于call
,否则等同于sendTransaction
。var winIdx = (Math.random() \* wheel.config.pieceCount) >> 0;
// 后台调用infura部署的合约必须用sendRawTransaction
var coder = require('web3/lib/solidity/coder');
var CryptoJS = require('crypto-js');
var Tx = require('ethereumjs-tx');
var privateKey = new Buffer("71112e795325d5cbf14d665091ce4626f26c8342b8038f1adcdfff26be04a220", 'hex');
var functionName = 'finishRound';
var types = ['uint'];
var args = [winIdx];
var fullName = functionName + '(' + types.join() + ')';
var signature = CryptoJS.SHA3(fullName, { outputLength: 256 }).toString(CryptoJS.enc.Hex).slice(0, 8);
var dataHex = signature + coder.encodeParams(types, args);
var data = '0x' + dataHex;
var account = "0x4BEB9EA54fc912B619D5C682BA1cB3524bc80955";
var nonce = web3.toHex(web3.eth.getTransactionCount(account));
var gasPrice = web3.toHex(web3.eth.gasPrice);
var gasLimitHex = web3.toHex(3000000);
var rawTx = { 'nonce': nonce, 'gasPrice': gasPrice, 'gasLimit': gasLimitHex, 'from': account, 'to': contractAddress, 'data': data }
var tx = new Tx(rawTx)
tx.sign(privateKey)
var serializedTx = '0x' + tx.serialize().toString('hex')
web3.eth.sendRawTransaction(serializedTx, function (err, txHash) {
if (!err) {
console.log(JSON.stringify({ "transactionHash": txHash }));
} else {
console.log("finish round error " + err);
}
});
sendRawTransaction
使用指定账户的私钥签名方法后才能调用合约,值得注意的是,该方法我们无法获得返回值,即使在event log中也拿不到,只能在得到transaction的hash后再读取区块链信息才可以,一般在后台调用方法时用到,代码如下。调用时需要注意的几点:
out of gas
的错误,这时候会回滚本地交易所做过的所有修改,好在以太坊还提供了estimategas
方法,可以让我们在调用之前预估交易所需的Gas,因为Solc编译器会算出每一句代码的价格,不过有时会不准确,特别是出现死循环或者违反了修饰方法的限制条件时。replacement transaction underpriced
错误。Fallback
方法作为兜底,而且会将Gas全耗完,如果没有定义Fallback
方法,才会报错回滚。在目前DApp应用市场上,目前主要有四类应用:去中心化交易所、集换类游戏、博彩类游戏和其他,比例如下图所示,其中集换式游戏以以太猫为首,这类游戏大部分以发行Token为噱头,因此本章节将讲述两种游戏类的DApp是如何开发的。
在笔者的前一篇介绍以太坊DApp的文章里,不少同事在评论都说到ICO,虽然这在中国是违法的,但技术还是可以学习一下的。其实,在以太坊上发行代币(Token)十分简单,流程就像开发一款DApp,比自己搭建一个新的区块链平台,实现加密、共识、网络问题简单得多。
以太坊提供了很多现成的合约,类似于模板,基于这些模板我们可以快速开发一款DApp。其中ERC20是最为常用的模板之一,它规定了发行代币所需要实现的所有方法,如下图所示,我们只需编写一个智能合约继承于ERC20,实现下列方法,部署到以太坊后就完成ICO流程了,当然,你想有人买你的代币,肯定要写一个白皮书啦。
from
,指向当前账户,该方法返回从from
账户转账_value
代币到_to
账户能否成功transfer
,返回从_from
转账_value
到_to
账户能否成功,但通常是第三方调用,即调用者账户地址既不等于_from
,也不等于_to
_value
代币给_spender
账户_owner
账户最多可以转账多少代币给_spender
账户,注意该方法用constant
修饰,所以实现代码不允许修改区块链上的数据Transfer
事件应该在transfer
和transferFrom
方法中被调用approve
方法中被调用另外值得注意的是,以太猫不是基于ERC20合约,而是ERC71,这合约是在ERC20的基础上,加上了唯一性的特性,因此以太猫游戏中每只猫(Token)都是独一无二的。
这是笔者开发的第一个比较完整的Demo,一个博彩类游戏,感兴趣的读者可以在传送门上体验,目前DApp是部署在Ropsten测试网络中,该测试网络的以太币可以免费获得,所以在上面的Demo尽情玩耍,感受以太坊交易的“快”感。demo源码地址: https://github.com/Dave1991/WheelOfFortune,运行方式就是在项目根目录执行npm start
命令。
首先需要安装MetaMask插件,然后刷新页面,点击某个扇区进行下注,下注之前可以看下页面上的信息,如上图所示,顶部显示了当前的奖金池(即过往未中奖的累计奖金),还有当前每一注的大小,注意这两项的单位都是wei,这是以太坊中最小的单位,与以太币的换算关系是1Ether = 10^18wei。右边显示了上轮的中奖情况,玩家可以通过统计每一轮开奖的扇区来计算开奖的规律(笑)。点击扇区下注时页面会弹出一个输入框输入下的注数,确定后将跳出MetaMask交易界面,如下图所示,由于我们每一注比较小,所以玩家可以下多一点注,不然交易的amount近似于0。当我们点击submit按钮后,交易就发生了,由于区块链同步时间较长,我们每次下注大约需要等几十秒左右才能确认下注成功,最后我们等待倒计时结束后系统将开奖。
整个Demo的架构如下:
三者的通信过程如下图所示:
0.后台启动轮盘倒计时
1.浏览器访问前端页面,向后台获取转盘的配置
2.前端页面启动倒计时
3.用户在前端页面下注,先请求以太坊节点,得到下注成功的返回再通知后台
3.5.后台倒计时时间到,请求以太坊节点,返回当前回合是否成功结束
4.前端倒计时也时间到了,向后台请求当前回合的开奖情况,但由于后台开奖结果需要等待以太坊节点的返回,因此这里需要轮询,要么就改成后台主动push到前端
注意,这里第3步前端下注的时候由于回调时间过长,可能会导致在后台开奖后还没有下注成功,这种情况下DApp应该要回滚这次下注,但这只是个Demo,就不要在意这些细节了。
讲到这里,相信读者已经清楚以太坊DApp的完整开发流程了,回顾一下这个流程,对于DApp开发和传统App开发的区别,读者总结出以下几点:
目前DApp的开发力度和普及程度还远远不够,在手机App面前如同九牛一毛,即使是去年最火爆的以太猫,平均日活也只有900多,原因笔者认为还是在于门槛太高(需要安装插件,每笔交易时间长,手续费高),希望号称下一代区块链平台的EOS能解决这些根本性问题。
本文对于以太坊DApp开发的介绍到此为止了,想继续深入研究的读者可参考下列链接:
最后的最后,笔者想抛一个问题,智能合约是否真的智能,真的公平呢?
笔者认为至少目前还没达到这个水平,原因刚才已经提到,合约结果的来源,也就是面对需要主观性的仲裁结果的时候,智能合约就无能为力了,这就大大限制了智能合约的使用范围,如果这个结果来自于业务后台(例如刚刚转盘的结果),那就很容易出现暗箱操作,毕竟业务后台的代码一般不会开源。针对这种状态,今年年初以太坊创始人提出了预言机的概念,一个可提供可信任、可验证结果的来源,相当于现实世界中的仲裁法庭,当合约发生冲突时能给出一个人人信服的结果,但这样区块链难以攻破的现状就不复存在了,预言机将成为区块链的短板,希望未来这一问题能够尽快解决,不然DApp实在难以普及。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。