1. 阅读本文前,你应该对以太坊、智能合约有所了解,如果你还不了解,建议你先看以太坊是什么
  2.  
  3. 除此之外,你最好还了解一些HTMLJavaScript知识。
  4. 本文通过实例教大家来开发去中心化应用,应用效果如图:

项目背景

Pete有一个宠物店,有16只宠物,他想开发一个去中心化应用,让大家来领养宠物。 
在truffle box中,已经提供了pet-shop的网站部分的代码,我们只需要编写合约及交互部分。

环境搭建

  1. 安装Node
  2. 安装 Truffle :npm install -g truffle
  3. 安装Ganache

*Ganache(或Ganache CLI)已经取代了 testrpc。

创建项目

1.建立项目目录并进入

  1. > mkdir pet-shop-tutorial
  2. > cd pet-shop-tutorial

2.使用truffle unbox 创建项目

  1. > truffle unbox pet-shop
  2. Downloading...
  3. Unpacking...
  4. Setting up...
  5. Unbox successful. Sweet!
  6. Commands:
  7. Compile: truffle compile
  8. Migrate: truffle migrate
  9. Test contracts: truffle test
  10. Run dev server: npm run dev

这一步需要等待一会 ,也可以使用truffle init 来创建一个全新的项目。

项目目录结构

  1. contracts/ 智能合约的文件夹,所有的智能合约文件都放置在这里,里面包含一个重要的合约Migrations.sol(稍后再讲)。
  2. migrations/ 用来处理部署(迁移)智能合约 ,迁移是一个额外特别的合约用来保存合约的变化。
  3. test/ 智能合约测试用例文件夹。
  4. truffle.js/ 配置文件。
  5. 其他代码可以暂时不用管。

编写智能合约

智能合约承担着分布式应用的后台逻辑和存储。智能合约使用solidity编写,可阅读 solidity系列文章 
在contracts目录下,添加合约文件Adoption.sol

  1. pragma solidity ^0.4.17;
  2. contract Adoption {
  3. address[16] public adopters; // 保存领养者的地址
  4. // 领养宠物
  5. function adopt(uint petId) public returns (uint) {
  6. require(petId >= 0 && petId <= 15); // 确保id在数组长度内
  7. adopters[petId] = msg.sender; // 保存调用这地址
  8. return petId;
  9. }
  10. // 返回领养者
  11. function getAdopters() public view returns (address[16]) {
  12. return adopters;
  13. }
  14. }

编译部署智能合约

Truffle集成了一个开发者控制台,可用来生成一个开发链用来测试和部署智能合约。

编译

Solidity是编译型语言,需要把可读的Solidity代码编译为EVM字节码才能运行。 
dapp的根目录pet-shop-tutorial下,

  1. > truffle compile

输出

  1. Compiling ./contracts/Adoption.sol...
  2. Writing artifacts to ./build/contracts

部署

编译之后,就可以部署到区块链上。 
在migrations文件夹下已经有一个1_initial_migration.js部署脚本,用来部署Migrations.sol合约。 
Migrations.sol 用来确保不会部署相同的合约。 
现在我们来创建一个自己的部署脚本2_deploy_contracts.js

  1. var Adoption = artifacts.require("Adoption");
  2. module.exports = function(deployer) {
  3. deployer.deploy(Adoption);
  4. };

在执行部署之前,需要确保有一个区块链运行 
可以使用Ganache来开启一个私链来进行开发测试,默认会在7545端口上运行一个开发链。 
Ganache 启动之后是这样:

接下来执行部署命令:

  1. > truffle migrate

执行后,有一下类似的输出:

  1. Using network 'develop'.
  2. Running migration: 1_initial_migration.js
  3. Deploying Migrations...
  4. ... 0x3076b7dac65afc44ec51508bf6f2b6894f833f0f9560ecad2d6d41ed98a4679f
  5. Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
  6. Saving successful migration to network...
  7. ... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
  8. Saving artifacts...
  9. Running migration: 2_deploy_contracts.js
  10. Deploying Adoption...
  11. ... 0x2c6ab4471c225b5473f2079ee42ca1356007e51d5bb57eb80bfeb406acc35cd4
  12. Adoption: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
  13. Saving successful migration to network...
  14. ... 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0
  15. Saving artifacts...

在打开的Ganache里可以看到区块链状态的变化,现在产生了4个区块。

这时说明已经智能合约已经部署好了。

测试

现在我们来测试一下智能合约,测试用例可以用 JavaScript or Solidity来编写,这里使用Solidity。 
在test目录下新建一个TestAdoption.sol,编写测试合约

  1. pragma solidity ^0.4.17;
  2. import "truffle/Assert.sol"; // 引入的断言
  3. import "truffle/DeployedAddresses.sol"; // 用来获取被测试合约的地址
  4. import "../contracts/Adoption.sol"; // 被测试合约
  5. contract TestAdoption {
  6. Adoption adoption = Adoption(DeployedAddresses.Adoption());
  7. // 领养测试用例
  8. function testUserCanAdoptPet() public {
  9. uint returnedId = adoption.adopt(8);
  10. uint expected = 8;
  11. Assert.equal(returnedId, expected, "Adoption of pet ID 8 should be recorded.");
  12. }
  13. // 宠物所有者测试用例
  14. function testGetAdopterAddressByPetId() public {
  15. // 期望领养者的地址就是本合约地址,因为交易是由测试合约发起交易,
  16. address expected = this;
  17. address adopter = adoption.adopters(8);
  18. Assert.equal(adopter, expected, "Owner of pet ID 8 should be recorded.");
  19. }
  20. // 测试所有领养者
  21. function testGetAdopterAddressByPetIdInArray() public {
  22. // 领养者的地址就是本合约地址
  23. address expected = this;
  24. address[16] memory adopters = adoption.getAdopters();
  25. Assert.equal(adopters[8], expected, "Owner of pet ID 8 should be recorded.");
  26. }
  27. }

Assert.sol 及 DeployedAddresses.sol是Truffle框架提供,在test目录下并不提供truffle目录。 
TestAdoption合约中添加adopt的测试用例

运行测试用例

在终端中,执行

  1. truffle test

如果测试通过,则终端输出:

  1. Using network 'develop'.
  2. Compiling ./contracts/Adoption.sol...
  3. Compiling ./test/TestAdoption.sol...
  4. Compiling truffle/Assert.sol...
  5. Compiling truffle/DeployedAddresses.sol...
  6. TestAdoption
  7. testUserCanAdoptPet (62ms)
  8. testGetAdopterAddressByPetId (53ms)
  9. testGetAdopterAddressByPetIdInArray (73ms)
  10. passing (554ms)

创建用户接口和智能合约交互

我们已经编写和部署及测试好了我们的合约,接下我们为合约编写UI,让合约真正可以用起来。 
在Truffle Box pet-shop里,已经包含了应用的前端代码,代码在src/文件夹下。 
在编辑器中打开src/js/app.js可以看到用来管理整个应用的App对象,init函数加载宠物信息,就初始化web3。 
web3是一个实现了与以太坊节点通信的库,我们利用web3来和合约进行交互。

初始化web3

接下来,我们来编辑app.js修改initWeb3():

删除注释,修改为:

  1. initWeb3: function() {
  2. // Is there an injected web3 instance?
  3. if (typeof web3 !== 'undefined') {
  4. App.web3Provider = web3.currentProvider;
  5. } else {
  6. // If no injected web3 instance is detected, fall back to Ganache
  7. App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
  8. }
  9. web3 = new Web3(App.web3Provider);
  10.  
  11. return App.initContract();
  12. }

代码中优先使用Mist 或 MetaMask提供的web3实例,如果没有则从本地环境创建一个。

实例化合约

使用truffle-contract会帮我们保存合约部署的信息,就不需要我们手动修改合约地址,修改initContract()代码如下:

  1. initContract: function() {
  2. // 加载Adoption.json,保存了Adoption的ABI(接口说明)信息及部署后的网络(地址)信息,它在编译合约的时候生成ABI,在部署的时候追加网络信息
  3. $.getJSON('Adoption.json', function(data) {
  4. // 用Adoption.json数据创建一个可交互的TruffleContract合约实例。
  5. var AdoptionArtifact = data;
  6. App.contracts.Adoption = TruffleContract(AdoptionArtifact);
  7.  
  8. // Set the provider for our contract
  9. App.contracts.Adoption.setProvider(App.web3Provider);
  10.  
  11. // Use our contract to retrieve and mark the adopted pets
  12. return App.markAdopted();
  13. });
  14. return App.bindEvents();
  15. }

处理领养

修改markAdopted()代码:

  1. markAdopted: function(adopters, account) {
  2. var adoptionInstance;
  3.  
  4. App.contracts.Adoption.deployed().then(function(instance) {
  5. adoptionInstance = instance;
  6.  
  7. // 调用合约的getAdopters(), 用call读取信息不用消耗gas
  8. return adoptionInstance.getAdopters.call();
  9. }).then(function(adopters) {
  10. for (i = 0; i < adopters.length; i++) {
  11. if (adopters[i] !== '0x0000000000000000000000000000000000000000') {
  12. $('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);
  13. }
  14. }
  15. }).catch(function(err) {
  16. console.log(err.message);
  17. });
  18. }

修改handleAdopt()代码:

  1. handleAdopt: function(event) {
  2. event.preventDefault();
  3.  
  4. var petId = parseInt($(event.target).data('id'));
  5.  
  6. var adoptionInstance;
  7.  
  8. // 获取用户账号
  9. web3.eth.getAccounts(function(error, accounts) {
  10. if (error) {
  11. console.log(error);
  12. }
  13.  
  14. var account = accounts[0];
  15.  
  16. App.contracts.Adoption.deployed().then(function(instance) {
  17. adoptionInstance = instance;
  18.  
  19. // 发送交易领养宠物
  20. return adoptionInstance.adopt(petId, {from: account});
  21. }).then(function(result) {
  22. return App.markAdopted();
  23. }).catch(function(err) {
  24. console.log(err.message);
  25. });
  26. });
  27. }

在浏览器中运行

安装 MetaMask

MetaMask 是一款插件形式的以太坊轻客户端,开发过程中使用MetaMask和我们的dapp进行交互是个很好的选择,安装完成后,浏览器工具条会显示一个小狐狸图标。

配置钱包

在接受隐私说明后,会出现页面如下:

这里我们通过还原一个Ganache为我们创建好的钱包,作为我们的开发测试钱包。点击页面的 Import Existing DEN,输入Ganache显示的助记词。

  1. candy maple cake sugar pudding cream honey rich smooth crumble sweet treat

然后自己想要的密码,点击OK。 
如图:

连接开发区块链网络

默认连接的是以太坊主网(左上角显示),选择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的工作目录。

  1. {
  2. "server": {
  3. "baseDir": ["./src", "./build/contracts"]
  4. }
  5. }

./src 是网站文件目录 
./build/contracts 是合约输出目录

以此同时,在package.json文件的scripts中添加了dev命令:

  1. "scripts": {
  2. "dev": "lite-server",
  3. "test": "echo \"Error: no test specified\" && exit 1"
  4. },

当运行npm run dev的时候,就会启动lite-server

启动服务

  1. > npm run dev

会自动打开浏览器显示我们的dapp,如本文的第一张图。 
现在领养一直宠物看看,当我们点击Adopt时,MetaMask会提示我们交易的确认,如图:

点击Submit确认后,就可以看到成功领养了这个宠物。 
在MetaMask中,也可以看到交易的清单:

好了,恭喜你,即将成为一名去中心化式应用开发者的你已经成为迈出了坚实的一步!

BIZZAN(币严) 数字货币交易所官方网址:

www.bizzan.com

[币严区块链]以太坊(ETH)Dapp开发入门教程之宠物商店领养游戏的更多相关文章

  1. [币严区块链]BitcoinCash - BCH钱包地址生成与扫块充值监控(JAVA版)

    本文的方案无需自建节点,因为BCH当前区块数据大小已经达到200G以上,BTC区块数据也已超过300G,若每个币都自建节点,对云服务器的消耗会非常大. 认识BitcoinCash(BCH) Bitco ...

  2. [币严区块链]简单易懂的以太坊(ETH)智能合约开发入门教程

    以太坊(Ethereum)是一提供个智能合约(smart contract)功能的公共区块链(BlockChain)平台. 本文介绍了一个简单的以太坊智能合约的开发过程. 开发环境 在以太坊上开发应用 ...

  3. [币严区块链]数字货币交易所之以太坊(ETH)钱包对接(四) 使用web3j对接以太坊钱包

    本文给大家介绍了 Web3j Java 版本的框架的基本使用,大家可根据本文的内容进行扩展性的练习,对其他 API 的使用进行尝试. 使用web3j对接以太坊钱包 一.开发准备事项 启动 Geth 此 ...

  4. [币严区块链]ETH搭建节点区块数据同步的三种模式:full、fast、light

    ETH  全节点Archive(归档)模式数据量增长图 上述图表可通过链接查看:https://etherscan.io/chartsync/chainarchive 通过上表,可以看到截止2019年 ...

  5. 关于书籍《区块链以太坊DApp开发实战》的内容告示

    书中所列举的以太坊 etherscan 浏览器,原链接已经不能使用国内网络正常访问了,需要翻墙,下面的链接不需要翻墙,也是 etherscan 的官方浏览器链接: 以太坊浏览器:https://cn. ...

  6. [币严区块链]USDT钱包节点搭建

    USDT是基于BTC发的稳定币,它是比特币的一条侧链,说简单点,就是在比特币区块数据的不可篡改性与区块唯一性的基础上,再封装了一层.具体原理可网上查资料.总之理解一点:USDT的钱包节点就是BTC的钱 ...

  7. [币严区块链]数字货币交易所之比特币(BTC)钱包对接 | 自建节点JSON-RPC访问

    BTC钱包对接流程 一.   部署BTC钱包节点 二.   分析BTC钱包的API 三.   通过JSON-RPC访问BTC钱包API 四.   部署测试 一.部署钱包节点 交易平台对接BTC之前,要 ...

  8. [币严区块链]交易所钱包系统如何实现USDT自动归集操作

    本文代码应用场景: 每个用户在交易所充值时,每个人都分配了独立的USDT地址,而交易所需要将所有独立的地址中USDT汇集到一个钱包地址(一般是冷钱包),从而实现资产归集与安全保障. 注意: 理解以下代 ...

  9. [币严区块链]数字货币交易所之瑞波(XRP)钱包对接

    对接Ripple(XRP),不需要本地部署钱包,直接访问Ripple API,本文包括访问Ripple API及如何免费获取测试的XRP. 对接流程 安装Ripple API Ripple API 接 ...

随机推荐

  1. 在MAC终端下打开Finder:

    在Terminal中打开Finder: open . 在Finder中打开Terminal: 系统偏好设置 -> 键盘 -> 快捷键 -> 服务,勾选「新建位于文件夹位置的终端窗口」

  2. 原生JavaScript(js)手把手教你写轮播图插件(banner)

    ---恢复内容开始--- 1.轮播图插件 1.什么是插件: 为已有的程序增加功能 2.插件的特点(为什么要做成一个插件)与注意事项: 1.通用性,可移植性强 2.兼容性:不会对其他代码产生影响 3.创 ...

  3. 深入分析Java的编译期与运行期

    不知大家有没有思考过,当我们使用IDE写了一个Demo类,并执行main函数打印 hello world时都经历了哪些流程么? 想通过这篇文章来分析分析Java的执行流程,或者换句话说想聊聊Java的 ...

  4. Python 竟能绘制如此酷炫的三维图

    通常我们用 Python 绘制的都是二维平面图,但有时也需要绘制三维场景图,比如像下面这样的: 这些图怎么做出来呢?今天就来分享下如何一步步绘制出三维矢量(SVG)图. 八面体 我们先以下面这个八面体 ...

  5. python方法的使用

    1.函数定义             def  函数名(形参):                     函数体                     return   返回值   2.函数执行   ...

  6. 浏览器DOM渲染及阻塞问题

    在准备面试,然后复习到了计网的知识点,紧接着又扯到了url从输入到浏览器渲染的那个问题,这里来顺便完善补充一下,本文的重点在渲染 上面的图就是浏览器从服务器请求来页面后渲染的全过程 这里我们分开来看: ...

  7. idea打包报错

    There is insufficient memory for the Java Runtime Environment to continue.# Native memory allocation ...

  8. JavaScript中一个方法同时发送两个ajax请求问题

    今天在做项目中遇到一个问题,大概是在一个jsp页面同时有一个select下拉搜索条件框和一个Bootstrap表格展示列表.这两个都要通过ajax向后台拿数据,而且要在页面加载时完成.当时的做法是: ...

  9. 第五章 函数day2

    5.2函数小高级 5.2.1 函数当参数 1 函数也可以当返回值 def v(dar): v = dar() def n (): print(444) v(n) # 实例2 def v(): prin ...

  10. HDU 6363

    题意略. 思路: 这里有两个结论需要注意: 1.gcd(a ^ x - 1,a ^ y - 1) = a ^ gcd(x,y) - 1 2.gcd(fib[x],fib[y]) = fib[gcd(x ...