区块链导论:以太坊DAPP(宠物店应用)

宠物领养应用是Truffle自带的一个教程应用,通过本教程你将学到以下内容:

  1. 使用Truffle Box来创建Truffle工程
  2. 编写智能合约
  3. 编译部署智能合约
  4. 测试智能合约
  5. 创建一个可与智能合约交互的用户接口
  6. 浏览器内访问DAPP

使用Truffle Box来创建Truffle工程

1
mkdir pet-shop

编写和测试智能合约

  1. 创建文件contracts/Adoption.sol

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    pragma solidity ^0.4.17;
    contract Adoption {
    address[16] public adopters;
    // 领养宠物
    function adopt(uint petId) public returns (uint) {
    require(petId >= 0 && petId <= 15);
    adopters[petId] = msg.sender;
    return petId;
    }
    // Retireving the adopters
    function getAdopters() public view returns (address[16]) {
    return adopters;
    }
    }
  2. 编写测试代码,’test/TestAdoption.sol’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
pragma solidity ^0.4.17;
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";
contract TestAdoption {
Adoption adoption = Adoption(DeployedAddresses.Adoption());
// Testing the adopt() function
function testUserCanAdoptPet() public {
uint returnedId = adoption.adopt(8);
uint expected = 8;
Assert.equal(returnedId, expected, "Adoption of pet ID 8 should be recorded.");
}
// Testing retrieval of all pet owners
function testGetAdopterAddressByPetIdInArray() public {
// Expected owner is this contract
address expected = this;
// Store adopters in memory rather than contract's storage
address[16] memory adopters = adoption.getAdopters();
Assert.equal(adopters[8], expected, "Owner of pet ID 8 should be recorded.");
}
}
  • Assert.sol:单元测试的assert方法,具体查看
  • DeployedAddresses.sol: 执行truffle测试用例,truffle会部署一个新的合约用力到区块链中,这个合约用户获得被部署的合约

执行测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
➜ pet-shop-tutorial truffle test
Using network 'development'.
Compiling ./contracts/Adoption.sol...
Compiling ./test/TestAdoption.sol...
Compiling truffle/Assert.sol...
Compiling truffle/DeployedAddresses.sol...
TestAdoption
✓ testUserCanAdoptPet (197ms)
✓ testGetAdopterAddressByPetId (165ms)
✓ testGetAdopterAddressByPetIdInArray (333ms)
3 passing (2s)

构建前端应用页面

  1. 打开/src/js/app.js,替换replace me代码段。

    • initWeb3

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

      判断web3实例是否存在(Mist或者MentaMask),如果存在则返回实例,不存在就新建。

    • initContract

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      $.getJSON('Adoption.json', function(data) {
      // 获取合约文件并使用truffle-contract方法实例化
      var AdoptionArtifact = data;
      App.contracts.Adoption = TruffleContract(AdoptionArtifact);
      // 将智能合约实例加载到服务容器中,此处是web3Providers
      App.contracts.Adoption.setProvider(App.web3Provider);
      // 使用合约来获取和标记被领养的宠物
      return App.markAdopted();
      });
    • markAdopted

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      var adoptionInstance;
      App.contracts.Adoption.deployed().then(function(instance) {
      adoptionInstance = instance;
      return adoptionInstance.getAdopters.call();
      }).then(function(adopters) {
      for (i = 0; i < adopters.length; i++) {
      if (adopters[i] !== '0x0000000000000000000000000000000000000000') {
      $('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);
      }
      }
      }).catch(function(err) {
      console.log(err.message);
      });

      使用getAdopters方法获得所有被领养的宠物,并将对应领养按钮设为disable

    • handleAdopt

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      var adoptionInstance;
      web3.eth.getAccounts(function(error, accounts) {
      if (error) {
      console.log(error);
      }
      // 获取当前账户
      var account = accounts[0];
      App.contracts.Adoption.deployed().then(function(instance) {
      adoptionInstance = instance;
      // Execute adopt as a transaction by sending account
      return adoptionInstance.adopt(petId, {from: account});
      }).then(function(result) {
      return App.markAdopted();
      }).catch(function(err) {
      console.log(err.message);
      });
      });

运行web server

1
npm run dev

完整代码

pet-shop

issue

  1. 修改DAPP的index.html,将<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>替换为https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js