一步步教你开发、部署第一个Dapp应用:宠物商店

今天我们来编写一个完整的去中心化应用(Dapps), 可以和编写智能合约结合起来看。

写在前面

阅读本文前,你应该对以太坊、智能合约有所了解,如果你还不了解,建议你先看以太坊是什么
除此之外,你最好还了解一些HTML及JavaScript知识。

本文通过实例教大家来开发去中心化应用,应用效果如图:

从本文,你可以学习到:

  • 搭建智能合约开发环境
  • 创建Truffle项目
  • 编写智能合约
  • 编译和部署智能合约到区块链
  • 如何通过Web3和智能合约交互
  • MetaMask 的使用

小专栏用户在教程结尾处可以下载完整的Dapp代码。

项目背景

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

环境搭建

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

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

创建项目

  1. 建立项目目录并进入
> mkdir pet-shop-tutorial
> cd pet-shop-tutorial
  1. 使用truffle unbox 创建项目
 > truffle unbox pet-shop
 Downloading...
 Unpacking...
 Setting up...
 Unbox successful. Sweet!

Commands:

  Compile:        truffle compile
  Migrate:        truffle migrate
  Test contracts: truffle test
  Run dev server: npm run dev

这一步需要等待一会

也可以使用truffle init 来创建一个全新的项目。

项目目录结构

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

其他代码可以暂时不用管

编写智能合约

智能合约承担着分布式应用的后台逻辑和存储。智能合约使用solidity编写,可阅读
本专栏solidity系列文章

在contracts目录下,添加合约文件Adoption.sol

pragma solidity ^0.5.0;

contract Adoption {

  address[16] public adopters;  // 保存领养者的地址

    // 领养宠物
  function adopt(uint petId) public returns (uint) {
    require(petId >= 0 && petId <= 15);  // 确保id在数组长度内

    adopters[petId] = msg.sender;        // 保存调用这地址 
    return petId;
  }

  // 返回领养者
  function getAdopters() public view returns (address[16] memory) {
    return adopters;
  }

}

编译部署智能合约

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

编译

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

> truffle compile

输出

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

部署

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

现在我们来创建一个自己的部署脚本2_deploy_contracts.js

var Adoption = artifacts.require("Adoption");

module.exports = function(deployer) {
  deployer.deploy(Adoption);
};

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

接下来执行部署命令:

> truffle  migrate

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

Using network 'develop'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x3076b7dac65afc44ec51508bf6f2b6894f833f0f9560ecad2d6d41ed98a4679f
  Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...
  ... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying Adoption...
  ... 0x2c6ab4471c225b5473f2079ee42ca1356007e51d5bb57eb80bfeb406acc35cd4
  Adoption: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Saving successful migration to network...
  ... 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0
Saving artifacts...

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

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

测试

现在我们来测试一下智能合约,测试用例可以用 JavaScript or Solidity来编写,这里使用Solidity。

test目录下新建一个TestAdoption.sol,编写测试合约

pragma solidity ^0.5.0;

import "truffle/Assert.sol";   // 引入的断言
import "truffle/DeployedAddresses.sol";  // 用来获取被测试合约的地址
import "../contracts/Adoption.sol";      // 被测试合约

contract TestAdoption {
  Adoption adoption = Adoption(DeployedAddresses.Adoption());

  // 领养测试用例
  function testUserCanAdoptPet() public {
    uint returnedId = adoption.adopt(8);

    uint expected = 8;
    Assert.equal(returnedId, expected, "Adoption of pet ID 8 should be recorded.");
  }

  // 宠物所有者测试用例
  function testGetAdopterAddressByPetId() public {
    // 期望领养者的地址就是本合约地址,因为交易是由测试合约发起交易,
    address expected = address(this);
    address adopter = adoption.adopters(8);
    Assert.equal(adopter, expected, "Owner of pet ID 8 should be recorded.");
  }

    // 测试所有领养者
  function testGetAdopterAddressByPetIdInArray() public {
  // 领养者的地址就是本合约地址
    address expected = address(this);
    address[16] memory adopters = adoption.getAdopters();
    Assert.equal(adopters[8], expected, "Owner of pet ID 8 should be recorded.");
  }
}

Assert.sol 及 DeployedAddresses.sol是Truffle框架提供,在test目录下并不提供truffle目录。

top Created with Sketch.