编辑 | kou
如何基于以太坊来编写去中心化应用?超全开发教程,手把手带你学起来!
福利福利!本文节选自《精通以太坊·智能合约开发》,文末免费送书,5本哟!
听听技术大咖们关于这本书的评价吧!
“本书完完全全是一本面向开发者的技术书籍,建议所有想加入区块链领域的开发者阅读。”
——西祠胡同创始人、FIBOS创始人 响马
“本书延续了《深入浅出区块链》的风格,并且对以太坊智能合约及DApp开发进行了极为系统、全面的介绍,强烈推荐初学者学习!”
——工信部区块链应用研究院副院长 赖一城
“希望这本书可以成为区块链世界的燎原火种。”
——PHP ConChina 联合创始人 锅巴GG
还等什么,这么好的书,赶紧读一读!
在去中心化应用中,发送给节点的请求通常被称为“交易”。交易和普通的请求有很大不同,即交易的数据经过用户个人签名之后发送到节点。
另外,普通的请求大多是同步的,而交易大多数都是异步的;交易不是使用普通的HTTP JSON请求,而是使用JSON RPC请求。所以,接下来,先谈一下,什么是JSON RPC?
JSON RPC
JSON RPC 介绍
JSON RPC(Remote Protocol Call)是一种以JSON为格式的远程调用协议,其请求和返回都是JSON格式,常见的请求格式如下:
jsonrpc定义了JSON RPC版本。method为调用方法名。params为传入的参数,若无参数则为null。id为调用标识符,可以为字符串。
返回也是JSON格式:
jsonrpc:定义JSON RPC版本。
result:方法返回值。error调用时错误,无错误时返回null,有错误时则返回一个错误对象。
id:调用标识符,与调用方传入的标识一致,当请求中的id检查发生错误时(转换错误/无效请求),则必须返回null。
关于 JSON RPC 的详细规范可以查阅说明文档(http://www.jsonrpc.org/ specification)。
如何与以太坊节点进行通信
以太坊使用JSON RPC 2.0规范来和节点进行通信,我们来看看这个步骤是怎样实现的:
首先要求我们在启动节点时,加入--rpc选项,如下所示。
geth 会默认使用8545进行监听JSON RPC请求,如果要更改端口,使用 --rpcport <portnumber> 向节点发送JSON RPC请求,这里以请求账户余额为例,方法为eth_getBalance进行说明。
在返回的结果中,可以从result里拿到余额,需要注意的是JSON里的数字是十六进制编码。
除 eth_getBalance 方法之外,常用的如发送交易(用于和合约互动或创建合约)的方法为 eth_sendTransaction,获取账号的方法为 eth_accounts,所有方法的使用可以通过 https://github.com/ethereum/wiki/wiki/JSON-RPC 查看。
Web3.js
通过使用JSON RPC虽然可以完成和节点的通信,但是这个过程需要和原始的底层数据交互,比较容易出错。Web3.js是以太坊官方的JavaScript SDK,可以帮助智能合约开发者使用HTTP或者IPC与本地的或者远程的以太坊节点进行交互。
当然Web3.js同样是使用JSON RPC和节点进行通信的。不过Web3.js提供了更友好的接口,实际上Web3.js就是一个库的集合,主要包括下面几个库:
web3-eth用来与以太坊区块链和智能合约交互。
web3-shh用来控制whisper协议与p2p通信,以及广播。
web3-bzz用来与swarm协议交互。
web3-utils包含了一些DApp开发应用的功能。
在geth中使用Web3.js
geth启动的时候会加载Web3.js库,因此可以在geth交互控制台里直接使用Web3.js。这在第9章介绍合约部署的时候已经使用过,之前使用的Web3.js提供的接口如下。
Web3.js API具体提供了哪些接口可以在文档
(http://web3js.readthedocs.io/ en/1.0/index.html)中查询到。
在应用中使用Web3.js
另一种方式是,在我们开发的应用中引入Web3.js库来和智能合约交互。
项目引入Web3.js
首先你需要将Web3引入到工程中,根据项目的不同,使用不同的方式。
npm,npm install Web3。
bower,bower install Web3。
meteor,meteor add ethereum:Web3。
vanilla,dist./Web3.min.js。
创建Web3实例
然后提供一个Provider来创建一个Web3的实例,为了不覆盖一个已有的Provider,需要先检查Web3实例是否已存在。因为在Mist中,在有MetaMask插件的浏览器中使用时会提供Provider。
创建实例的方法如下:
创建好Web3对象后,就可以使用Web3.js 提供的API了。Web3.js所有的API可以在网站(http://web3js.readthedocs.io/en/1.0/index.html)上查询。
使用回调
由于Web3.js API被设计用来与本地的RPC结点交互,所以所有函数默认使用同步的HTTP请求。
如果想发起一个异步的请求,那么大多数函数允许传一个跟在参数列表后的可选的回调函数来支持异步。回调函数支持错误优先的回调模式(Error First Callback)。例如:
批量请求
可以允许将多个请求放入队列并一起执行,方法如下所示。
注意:批量请求并不会更快,批量请求的主要目的是用来保证请求的串行执行。实际上同时发起多个请求会更快,因为请求是异步处理的。
处理大数据
数据类型的返回结果,得到一个BigNumber对象,因为JavaScript不能正确地处理BigNumber,如下所示。
所以Web3.js依赖BigNumber 库,并且会自动进行引入,如下所示。
再看下面一个例子,即使有20位以上的浮点值,也会出错。所以,尽量让账户余额以wei为单位,仅仅在需要向用户展示时,才转换为其他单位。
去中心化应用案例
我们结合一个完整的案例来说明Web3.js在去中心化应用中的使用。下面是一个Web应用,其开发完成之后的界面,如下所示。
这个应用的名字(Name)和年龄(Age)数据是存贮在区块链上的。我们来看看是如何实现的。
搭建测试环境
在开发初期,我们没有必要使用真实的公链。为了开发效率,一般选择在本地搭建环境。
这里使用Ganache(http://truffleframework.com/ganache/),它可以提供一个模拟的以太坊节点环境(Ganache CLI是它的命令行版本),安装之后,打开的界面如下所示。
从图中可以看到Ganache会默认创建10个账户,监听地址是http://127. 0.0.1:7545,可以实时看到Current Block、Gas Price、Gas Limit等信息。
创建智能合约
打开Remix IDE(https://remix.ethereum.org),
在代码区域编写智能合约代码:
合约代码很简单,name和age是存在区块链中的状态变量,函数setInfo和getInfo用来进行变量赋值与读取。
接下来把Remix IDE右侧功能区域切换到run的tab下,将Environment切换成Web3 Provider,并输入Ganache 提供的RPC地址http://127.0.0.1:7545。下面对这三个选项进行一下说明:
JavaScript VM:提供JavaScript虚拟机环境。
Injected Web3:连接到嵌入页面的Web3,比如连接到MetaMask。
Web3 Provider:连接到自定义的节点。
连接成功后,下面的Account选项会默认选择Ganache创建的第一个账户地址,如下图所示。
点击Create,就会将智能合约部署到我们的测试环境中,如下图所示。
智能合约部署之后,接下来要编写应用UI及跟合约交互的部分。
创建项目,安装Web3
先创建应用的项目目录,使用以下命令。
使用node.js(安装Node)的包管理工具 npm 初始化项目。
在命令运行期间,输入项目名称、版本等信息。项目创建完成后,生成一个package.json文件,保存项目信息及相关依赖。
然后运行命令,安装Web3.js。
注意:在实际安装过程中,我发现Web3在安装完成后并没有/node_modules/ web3/dist/web3.min.js文件。这个问题在issue#1041(https://github.com/ethereum/ web3.js/issues/1041)有提到,但官方好像一直没解决。不过我们可以在这里下载所需的文件,解压后将dist文件夹的内容拷贝到/node_modules/web3路径下。
创建UI
在项目目录下创建index.html文件,在这里编写基础的UI。UI包括name和age的输入框,以及一个按钮。这里也会引入jQuery进行一些交互,index.html代码如下:
接下来需要编写main.css文件,设定基本的样式。
使用Web3与智能合约交互
UI 创建好之后,在<script>标签中间编写Web3.js的代码与智能合约交互,关键的步骤和代码如下。
创建Web3实例,并且与Ganache 提供的测试环境连接
使用Web3 API设置默认的账户。Ganache 帮我们创建了10个账户,这里选择第一个账户当作默认账户
创建合约实例
这里需要用到合约的ABI JSON,它包含一系列的函数或事件的描述。我们可以在Compile的tab下点击Details,在出现的页面中拷贝合约的ABI(点击“复制”小图标),如下图所示。
将其复制到代码中,代码如下所示。
接着在Remix Run 标签中也可以拷贝合约的地址,将其复制到下面的代码中。
这时就完成了合约实例的创建。
合约函数交互
使用合约实例调用合约中的函数。下面我们使用jQuery与合约进行交互。
以上代码实现了对合约中两个函数的调用,分别读取和显示name和age变量。到此我们就完成了应用的全部代码,完整代码可以在GitHub(https://github.com/ xilibi2003/InfoContract)查看。
在浏览器中打开index.html,输入名字和年龄后刷新一下,就可以看到前面贴出的效果图了。
合约加入事件
在上面的案例中,只有在刷新浏览器后才能看到数据的变化。如果要监听数据的变化,则需要在合约中发送事件。
先在合约中声明一个事件,如下所示。
在这个事件中会接收两个参数name和age,也就是需要跟踪的两个信息。然后在setInfo函数中,触发Instructor事件,如下所示。
使用Web3监听事件、刷新UI
点击“Updata Info”按钮之后,会调用setInfo函数,触发Instructor事件。现在使用Web3监听事件,刷新UI。
在之前的js代码中,我们使用info.getInfo()来获取信息,现在我们改用监听事件获取信息。先定义一个变量引用事件,如下所示。
然后使用.watch()方法来添加一个回调函数,如下所示。
大家可以在我的GitHub上查看到完整的代码,地址为https://github.com/ xilibi2003/InfoContract。
Truffle框架
上面我们介绍了如何开发去中心化应用。但是如果项目大一些,则需要不停地进行智能合约编译、部署、测试,这会让项目很难管理。这时就可以使用Truffle来进行开发了。
Truffle是目前最流行的以太坊开发框架,它可以帮我们处理掉大量开发中的琐事,让我们可以迅速开始写代码—编译—部署—测试—打包DApp这一整个流程。
使用以下命令安装Truffle。
Truffle使用案例
我们结合案例来看看是如何使用Truffle框架编写去中心化应用的。这个应用写的是一个宠物店,我们在应用中卖宠物。用区块链记录宠物的领养数据,应用效果如下图所示。
创建项目
首先建立项目目录并进入,代码如下所示。
使用Tuffle Unbox初始化项目。针对不同类型的应用,Truffle提供了一些Box。我们创建好了一些示例代码,大家可以通过这个网址(http:// truffleframework.com/ boxes/)查看到Truffle提供的Box情况。
这个就像很多IDE在新建工程时的引导一样,给我们提供了一些示例代码。
本节介绍的Pet Shop应用,已经为我们提供了网站代码,我们只需要编写合约及交互部分即可。通过truffle unbox pet-shop来初始化项目,执行成功后输入代码。
如果想从头创建一个项目,也可以在项目目录下,执行truffle init来初始化一个全新的项目。
项目目录结构
Truffle生成的项目目录结构如下。
contracts为智能合约的文件夹,所有的智能合约文件都放置在这里。
migrations是用来处理部署(迁移)智能合约的。迁移是用一个额外、特别的合约来保存的。
test智能合约测试用例文件夹。
truffle.js配置文件。
src web源码文件夹。
编写智能合约
接下来,编写智能合约。在contracts目录下,添加合约文件Adoption.sol。
智能合约很简单,用状态变量adopters来保存每个领养者的地址。
智能合约编译
Truffle集成了一个开发者控制台,在项目目录下运行。
输出如下命令。
存在build/contracts/ Adoption.json上。
智能合约部署
编译之后,就可以部署到区块链上了。
在migrations文件夹下已经有一个1_initial_migration.js部署脚本,用来部署Migrations.sol合约。
Migrations.sol 是Truffle生成的一个用来确保不会重复部署的合约,这个合约可以在contracts找到。我们来创建一个自己的部署脚本2_deploy_contracts.js。
同样,和本章介绍的第一个案例一样。在执行部署之前,需要确保有一个区块链环境在运行。使用Ganache来启动一个模拟开发链,接下来执行部署命令。
执行后,有以下类似的输出。
智能合约部署好后,可以看到Ganache里区块链状态发生了变化,产生了新区块。
智能合约测试
我们在开发应用的时候,还有很重要的一步要做,那就是进行测试。测试用例可以用JavaScript或Solidity来编写,这里使用Solidity。
在test目录下新建一个TestAdoption.sol,编写测试合约代码如下。
Assert.sol及DeployedAddresses.sol是由Truffle框架提供的。在test目录下不提供Truffle目录。
TestAdoption测试合约同样也很简单,大家直接阅读注释就可以理解。
运行测试用例
在终端中执行:
如果测试通过,则终端输出:
创建用户接口和智能合约交互
我们已经编写、部署及测试了智能合约。现在我们为合约编写UI,让UI和合约能真正交互起来。如果是使用truffle unbox pet-shop初始化的工程,那么已经包含了应用的前端代码。代码在src/文件夹下。在编辑器中打开src/js/app.js可以看到用来管理整个应用的App对象。其中init函数可以加载宠物信息,并且初始化Web3。
初始化Web3
编辑app.js修改initWeb3()函数,删除注释。
同样,在代码中优先使用Mist或MetaMask提供的Provider,如果没有则从本地环境创建一个。
实例化合约
Truffle会帮我们保存合约部署的信息,所以不用像本章第一个案例那样手动填写合约地址,修改initContract()代码如下。
处理领养
修改函数 markAdopted()代码。
修改函数handleAdopt()代码。
在浏览器中运行
之前安装过MetaMask的用户可以直接使用MetaMask账号导入功能,用Ganache提供的私钥导入对应的账号。
连接开发区块链网络
默认连接的是以太坊主网(左上角显示),选择Custom RPC,添加一个网络地址http://127.0.0.1:7545,点击返回后,显示如下。
左上角显示为Private Network,账号是Ganache中默认的第一个账号。
至此MetaMask的安装配置已经完成。
安装和配置lite-server
接下来需要本地的Web服务器提供服务访问。Truffle Box pet-shop里提供了一个lite-server可以直接使用,我们看看它是如何工作的。bs-config.json指示了lite-server的工作目录。
./src 是网站文件目录。./build/contracts 是合约输出目录。
与此同时,在package.json文件的scripts中添加了dev命令。
当运行npm run dev的时候,就会启动lite-server。
启动服务
启动服务代码,如下所示。
自动打开浏览器显示我们的DApp。
现在领养一只宠物看看,当我们点击Adopt时,MetaMask会提示确认交易,如下图所示。
点击SUBMIT确认后,就可以看到我们已成功领养了一只宠物。
在MetaMask中,也可以看到交易的清单,如下图所示。
至此,去中心化式应用完整的开发过程就完成了。
通过本章的介绍,大家可以了解到去中心化应用DApp开发的思路及流程。在DApp开发中,以太坊的节点充当了应用的后台服务(或后台数据库服务),希望本章的案例能给大家开发DApp带来启发。
这么详细的以太坊开发教程,小伙伴儿们还等什么?赶紧学起来!
--【完】--
感谢博文视点赞助
关于以太坊、智能合约和DApp开发,请在文末畅所欲言,留言获点赞数前5的读者就可获得本书呦!
送书截止时间10月19日(本周五)中午12点!每周五晚上8点,小编准时送书,不见不散哟!
最新热文:
大力戳↑↑↑ 加入区块链大本营读者⑦号群
(内容转载请联系微信:CSDN_qkldby)
(商务合作请联系微信:fengyan-1101)
免费公开课,快来报名!