第七課 技術小白如何開發一個DAPP區塊鏈應用(以寵物商店爲例)

1. 文章摘要

【本文目標】

經過逐步的指導和截圖舉證,一步步帶領一個技術小白完成一個寵物商店DAPP應用的開發和部署。css

【環境前置條件】

參考《第一課 如何在WINDOWS環境下搭建以太坊開發環境》,已完成Ubuntu的安裝,已完成TRUFFLE,Ganache-cli,lite-server的安裝; 本案例是經過WINDOWS的XSHELL客戶端同本機的Ubuntu命令操做進行的。 已在本地WIDOWS環境完成MetaMask輕錢包客戶端的安裝。 最好遵循從頭開始的課程學習順序。不過若是你想半途插入實操學習,問題也不大,遇到障礙時反向找對應文章的指導內容便可完成。html

【技術收穫】

從本實踐中,你能夠學習到: 搭建智能合約開發環境 建立Truffle項目 編寫智能合約 編譯和部署智能合約到區塊鏈 如何經過Web3和智能合約交互 MetaMask 的使用前端

【實操課程列表】

第一課 如何在WINDOWS環境下搭建以太坊開發環境node

第二課 如何實現以太坊最簡智能合約「Hello World」的運行jquery

第四課 以太坊開發框架Truffle從入門到實戰git

第六課 技術小白如何開發一個DAPP區塊鏈應用(以寵物商店爲例)github

第七課 技術小白如何在45分鐘內發行通證(TOKEN)並上線交易web

第八課 如何調試以太坊官網的智能合約衆籌案例ajax

【說明】未列出的課程爲知識普及的非實操類課程,全部區塊鏈文章參考「區塊鏈入口」專欄。npm

2. 下載/編寫TRUFFLE框架的智能合約

項目背景

Pete有一個寵物店,有16只寵物狗,他想開發一個去中心化應用,讓你們來領養寵物。 在truffle box中,已經提供了pet-shop的網站部分的代碼,咱們只須要編寫合約及交互部分。 【官網原始參考】 代碼框架下載:truffleframework.com/boxes/pet-s… 開發指導:truffleframework.com/tutorials/p…

環境搭建

環境須要NodeJS, Truffle,Ganache-Cli, Lite-Server, Meta-Mask等程序,按照步驟能夠參考《第一課 如何在WINDOWS環境下搭建以太坊開發環境》 搜索對應關鍵字在已安裝的UBUNTU操做系統完成安裝。

  1. 安裝Node:

sudo apt-get install curl curl -sL deb.nodesource.com/setup_6.x | sudo -E bash sudo apt-get install -y nodejs

  1. 安裝 Truffle :

npm install -g truffle

  1. 安裝Ganache

sudo npm install -g ganache-cli

【說明】Ganache(或Ganache CLI)已經取代了 testrpc。

4.Lite-Server, Meta-Mask安裝

本文後面章節描述。

建立項目

  1. 創建項目目錄並進入
mkdir dapp-guide-pet-shop
 cd dapp-guide-pet-shop

複製代碼
  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
複製代碼

項目目錄結構

contracts/ 智能合約的文件夾,全部的智能合約文件都放置在這裏,裏面包含一個重要的合約Migrations.sol(稍後再講) migrations/ 用來處理部署(遷移)智能合約 ,遷移是一個額外特別的合約用來保存合約的變化。 test/ 智能合約測試用例文件夾 truffle.js/ 配置文件 其餘代碼能夠暫時不用管 【說明】若是想了解TRUFFLE框架更詳細的內容,可參考文章《第四課 以太坊開發框架Truffle從入門到實戰》

編寫智能合約

智能合約承擔着分佈式應用的後臺邏輯和存儲。智能合約使用solidity編寫。

在contracts目錄下,添加合約文件Adoption.sol

pragma solidity ^0.4.17;

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]) {
    return adopters;
  }

}

複製代碼

編譯部署智能合約

Truffle集成了一個開發者控制檯,可用來生成一個開發鏈用來測試和部署智能合約。

編譯

Solidity是編譯型語言,須要把可讀的Solidity代碼編譯爲EVM字節碼才能運行。 進入dapp的根目錄dapp-guide-pet-shop執行命令,

> 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測試環境

在執行部署以前,須要確保有一個區塊鏈運行, 可使用 Ganache來開啓一個私鏈來進行開發測試。 參考文章《第一課 如何在WINDOWS環境下搭建以太坊開發環境》 對應的章節,完成ganache-cli的安裝。

sudo npm install -g ganache-cli

在新的命令行窗口運行ganache-cli程序:

cd /usr/work/Ganache ganache-cli >> trace.log

這個>>目的是把ganache-cli的結果輸出到文件,便於後面複製錢包地址用於查看ETH餘額。做者實踐時把這2行命令合併成一行執行。

ganache-cli >> /usr/work/Ganache/trace.log

這個命令窗口用於運行Ganache程序輸出,不可關閉。須要新開一個命令窗口用於執行TRUFFLE的命令。

輸出截圖
打開trace.log文件,可查看默認啓動的10個錢包帳戶和對應的私鑰地址:

Available Accounts
==================
(0) 0x7554cc8c721712adde43e67a5669225bbe8f21f6
(1) 0x3e6ce6a43c1fa565b5b90963bef090625d3edc6c
(2) 0xeb2eb6ed4b325e77f5a597497ec5ffaa2f5c2650
(3) 0x1de061d5f225533f7e3c38a5905a6ca2ecb3e55a
(4) 0x3ec2e1ed3f47fc7ab9cca1fe09afe9fd1feb789b
(5) 0xf27a303880b73a0a287a2e5dc1286098fb49ed63
(6) 0xf3c007932e1de894503166aee3cbf85b4aff0188
(7) 0xe7655733659c14c7c83fb71bd40dc51796592d96
(8) 0x507722000223ca96ac646198709b9ae3f7f49a5a
(9) 0x82b087a83f72cc7dd5ca6ac7787a366b2c3ff143

Private Keys
==================
(0) 629551aa45c594ce822c5b4a378d01cf46fb57c15b69a61eb400a4867ffab002
(1) a90103a95ed805acc52782eaa29eb061f6c2a9431fed3ac18a683ea3143a29b6
(2) c2b13ce370fb235997dc4783ce591a22e5b3909e934dc7c0f61797ce57d5059d
(3) 841d0e1d3dc658ad2f308c7292b4d5c40da158d170cf3ed9001c64b8352cd0c2
(4) 255225aedba340b6ec62e6c86a6202535d7382704b129d022461c69b8341d2dd
(5) c3fd784fc7a46edb5e7cba9e90120e51152a3fbe2d8b97a0c3106791d3bbe87e
(6) f8f90b056c419464d48dcd12ac8e326a31ebe300ea5e245876fe0308c511fbac
(7) 09e20553971b044c1fda8ac88f2ed1dcdff6af0889fc05c2ad4374c52d4f52d7
(8) 142643d0fb6f4a78e0fd291a568b784869927d8f8eee87bf3fbdbf493ec3f425
(9) 8f89d4a05582193cc32f800ecf9419f35b5384abc5db42afd4ec016a54d27716
複製代碼

配置以太坊客戶端本地環境

ruffle.js是truffle的配置文件,位於dapp-guide-pet-shop目錄下,啓動好以太坊本地結點之後,咱們須要讓truffle去識別它並使用它,這就須要在truffle.js中配置相關屬性:

module.exports = {
  // See <http://truffleframework.com/docs/advanced/configuration>
  // for more about customizing your Truffle configuration!
  networks: {
    development: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "*" // Match any network id
    }
  }
};
複製代碼

執行部署命令

接下來在新的命令窗口執行部署命令

cd /usr/work/dapp-guide-pet-shop truffle migrate

執行後,有一下相似的輸出,

Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x29612ceea67bc946cc6ae82afbedc546f9a53ba8cab5d804f9025fb8f15e48f8
  Migrations: 0x8af912046664ba26738b811c34068d42216528c0
Saving successful migration to network...
  ... 0x6c968a3c492439ab22028e1956360a6b73a02716c436b15234cd29804dac7298
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying Adoption...
  ... 0x27448e4f75b608015f3670e3650cf607c882fe7f1a32f98e8a94bf7a406c871c
  Adoption: 0xe42f434105a7e0eacf4f4229c76e1e135d536db6
Saving successful migration to network...
  ... 0x9375cd6fa143d01a520c5ed0d46cfe859f482b01e37cf678d751c5db6a278e5f
Saving artifacts...

複製代碼

查看Ganache-cli的輸出文件:trace.log,能夠看到區塊鏈狀態的變化,如今產生了4個區塊。

net_version
eth_accounts
eth_accounts
net_version
net_version
eth_sendTransaction

  Transaction: 0x29612ceea67bc946cc6ae82afbedc546f9a53ba8cab5d804f9025fb8f15e48f8
  Contract created: 0x8af912046664ba26738b811c34068d42216528c0
  Gas usage: 268407
  Block Number: 1
  Block Time: Tue Apr 10 2018 09:10:18 GMT+0800 (CST)

eth_newBlockFilter
eth_getFilterChanges
eth_getTransactionReceipt
eth_getCode
eth_uninstallFilter
eth_sendTransaction

  Transaction: 0x6c968a3c492439ab22028e1956360a6b73a02716c436b15234cd29804dac7298
  Gas usage: 41981
  Block Number: 2
  Block Time: Tue Apr 10 2018 09:10:19 GMT+0800 (CST)

eth_getTransactionReceipt
eth_accounts
net_version
net_version
eth_sendTransaction

  Transaction: 0x27448e4f75b608015f3670e3650cf607c882fe7f1a32f98e8a94bf7a406c871c
  Contract created: 0xe42f434105a7e0eacf4f4229c76e1e135d536db6
  Gas usage: 247573
  Block Number: 3
  Block Time: Tue Apr 10 2018 09:10:19 GMT+0800 (CST)

eth_newBlockFilter
eth_getFilterChanges
eth_getTransactionReceipt
eth_getCode
eth_uninstallFilter
eth_sendTransaction

  Transaction: 0x9375cd6fa143d01a520c5ed0d46cfe859f482b01e37cf678d751c5db6a278e5f
  Gas usage: 26981
  Block Number: 4
  Block Time: Tue Apr 10 2018 09:10:20 GMT+0800 (CST)

eth_getTransactionReceipt
複製代碼

這時說明已經智能合約已經部署好了。

測試

如今咱們來測試一下智能合約,測試用例能夠用 JavaScript or Solidity來編寫,這裏使用Solidity。

test目錄下新建一個TestAdoption.sol,編寫測試合約

pragma solidity ^0.4.17;

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 = this;
    address adopter = adoption.adopters(8);
    Assert.equal(adopter, expected, "Owner of pet ID 8 should be recorded.");
  }

    // 測試全部領養者
  function testGetAdopterAddressByPetIdInArray() public {
  // 領養者的地址就是本合約地址
    address expected = 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目錄。

TestAdoption合約中添加adopt的測試用例

運行測試用例

在終端中,執行

truffle test

複製代碼

若是測試經過,則終端輸出:

Using network 'development'.

Compiling ./contracts/Adoption.sol...
Compiling ./test/TestAdoption.sol...
Compiling truffle/Assert.sol...
Compiling truffle/DeployedAddresses.sol...

Compilation warnings encountered:

truffle/Assert.sol:1563:9: Warning: Use of the "var" keyword is deprecated.
        var nstr = _itoa(value, 10);
        ^------^
,truffle/Assert.sol:1580:9: Warning: Use of the "var" keyword is deprecated.
        var nstr = _utoa(value, 10);
        ^------^
,truffle/Assert.sol:1597:9: Warning: Use of the "var" keyword is deprecated.
        var nstr = _ltoa(value);
        ^------^
,truffle/Assert.sol:1347:13: Warning: Invoking events without "emit" prefix is deprecated.
            TestEvent(true, "");
            ^-----------------^
,truffle/Assert.sol:1349:13: Warning: Invoking events without "emit" prefix is deprecated.
            TestEvent(false, message);
            ^-----------------------^

  TestAdoption
    ✓ testUserCanAdoptPet (246ms)
    ✓ testGetAdopterAddressByPetId (231ms)
    ✓ testGetAdopterAddressByPetIdInArray (287ms)


  3 passing (2s)
複製代碼

3. 建立用戶接口和智能合約交互

咱們已經編寫和部署及測試好了咱們的合約,接下咱們爲合約編寫UI,讓合約真正能夠用起來。

在Truffle Box pet-shop裏,已經包含了應用的前端代碼,代碼在src/文件夾下。

在編輯器中打開src/js/app.js 能夠看到用來管理整個應用的App對象,init函數加載寵物信息,就初始化web3. web3是一個實現了與以太坊節點通訊的庫,咱們利用web3來和合約進行交互。

初始化web3

接下來,咱們來編輯app.js修改initWeb3(): 刪除註釋,修改成:

initWeb3: function() {
    // 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:8545');
    }
    web3 = new Web3(App.web3Provider);

    return App.initContract();
  }

複製代碼

代碼中優先使用 MetaMask提供的web3實例,若是沒有則從本地環境建立一個。

實例化合約

使用truffle-contract會幫咱們保存合約部署的信息,就不須要咱們手動修改合約地址,修改initContract()代碼以下:

initContract: function() {
  	// 加載Adoption.json,保存了Adoption的ABI(接口說明)信息及部署後的網絡(地址)信息,它在編譯合約的時候生成ABI,在部署的時候追加網絡信息
    $.getJSON('Adoption.json', function(data) {
      // Get the necessary contract artifact file and instantiate it with truffle-contract
       // 用Adoption.json數據建立一個可交互的TruffleContract合約實例。
      var AdoptionArtifact = data;
      App.contracts.Adoption = TruffleContract(AdoptionArtifact);
    
      // Set the provider for our contract
      App.contracts.Adoption.setProvider(App.web3Provider);
    
      // Use our contract to retrieve and mark the adopted pets
      return App.markAdopted();
    });

    return App.bindEvents();
  },

複製代碼

處理領養

修改markAdopted()代碼:

markAdopted: function(adopters, account) {
    var adoptionInstance;

    App.contracts.Adoption.deployed().then(function(instance) {
      adoptionInstance = instance;

      // 調用合約的getAdopters(), 用call讀取信息不用消耗gas
      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);
    });
  }

複製代碼

修改handleAdopt()代碼:

handleAdopt: function(event) {
    event.preventDefault();

    var petId = parseInt($(event.target).data('id'));

    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;

        // 發送交易領養寵物
        return adoptionInstance.adopt(petId, {from: account});
      }).then(function(result) {
        return App.markAdopted();
      }).catch(function(err) {
        console.log(err.message);
      });
    });
  }
複製代碼

4. 安裝和配置lite-server

【定義】lite-server 是一個全功能的網站架設工具軟件包輕量級的,僅適用於開發 的 node 服務器, 它僅支持 web app。 它可以爲你打開瀏覽器, 當你的html或是JavaScript文件變化時,它會識別到並自動幫你刷新瀏覽器, 還能使用套接字自動注入變化的CSS, 當路由沒有被找到時,它將自動後退頁面。 參考文章《第一課 如何在WINDOWS環境下搭建以太坊開發環境》的"(8)安裝 lite-server 【可選】」章節完成lite-server的安裝。

bs-config.json文件指示了lite-server的工做目錄。

{
  "server": {
    "baseDir": ["./src", "./build/contracts"]
  }
}
複製代碼

./src 是網站文件目錄 ./build/contracts 是合約輸出目錄

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

{
  "name": "pet-shop",
  "version": "1.0.0",
  "description": "",
  "main": "truffle.js",
  "directories": {
    "test": "test"
  },
  "scripts": {
    "dev": "lite-server",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "lite-server": "^2.3.0"
  }
}
複製代碼

當在新的命令窗口運行npm run dev的時候,就會啓動lite-server

cd /usr/work/dapp-guide-pet-shop npm run dev

正常的運行結果有以下相似輸出內容:

> pet-shop@1.0.0 dev /usr/work/dapp-guide-pet-shop
> lite-server

** browser-sync config **
{ injectChanges: false,
  files: [ './**/*.{html,htm,css,js}' ],
  watchOptions: { ignored: 'node_modules' },
  server: 
   { baseDir: [ './src', './build/contracts' ],
     middleware: [ [Function], [Function] ] } }
[Browsersync] Access URLs:
 ---------------------------------------
       Local: http://localhost:3000
    External: http://192.168.80.144:3000
 ---------------------------------------
          UI: http://localhost:3001
 UI External: http://192.168.80.144:3001
 ---------------------------------------
[Browsersync] Serving files from: ./src
[Browsersync] Serving files from: ./build/contracts
[Browsersync] Watching files...
複製代碼

5. 安裝 MetaMask和配置區塊鏈網絡

安裝 MetaMask

【定義】MetaMask 是一款插件形式的以太坊輕客戶端,開發過程當中使用MetaMask和咱們的dapp進行交互是個很好的選擇。 做者是在本地WINDOWS的CHROME瀏覽器上安裝MetaMask錢包工具,具體的安裝方法參考文章《第一課 如何在WINDOWS環境下搭建以太坊開發環境》的「(7)安裝 MetaMask 【可選】」章節。 說明下,最詳細的MetaMask安裝/配置文章可參考歐陽哥哥的《以太坊錢包MetaMask使用教程》

配置錢包

1. 接受隱私條款 點擊瀏覽器地址欄右側,MetaMask的狐狸頭圖標,第一次使用時,會出一個隱私提示,以下圖:

隱私提示

2. 接受服務條款 點擊Accept按鈕,顯示的是MetaMask的服務條款,以下圖2-2:

image.png

Accept按鈕默認是灰色的,將滾動條拉到底部,就能夠點擊Accept按鈕了

三、建立新帳號

  • 建立新帳號:輸入一串8位以上的密碼,再次重複輸入,點擊CREATE按鈕,便可完成。 下面以建立新帳號爲例:
    做者啓動大寫鍵使用本身的通用密碼設置

在點擊CREATE按鈕後,MetaMask會爲用戶建立12個英文助記詞,必定要保存好這些助記詞,點擊SAVE SEED WORDS AS FILE能夠將助詞詞以文件的形式保存到本地,建議使用紙筆手工記錄並收藏於安全的地方。

SEED

鏈接開發區塊鏈網絡

默認鏈接的是以太坊主網(左上角顯示),選擇Custom RPC,添加Ubuntu的IP地址做爲客戶自定義RPC網絡

http://192.168.80.144:8545

連接測試機以太坊客戶端成功,菜單有連接成功的橙色√提示。

鏈接成功
這是左上角顯示爲 Private Network,此時顯示的是默認的Account 1空帳號,ETH數量爲0。

導入Ganache-cli的第一個錢包帳號

查看「trace.log」文件,能夠Ganache-cli的第一個默認錢包地址爲

(0) 629551aa45c594ce822c5b4a378d01cf46fb57c15b69a61eb400a4867ffab002
(1) a90103a95ed805acc52782eaa29eb061f6c2a9431fed3ac18a683ea3143a29b6
複製代碼

選擇MetaMask的「Import Account」輸入私鑰,獲取帳號,能夠查看對應ETH餘額。

輸入私鑰
第一個錢包帳戶的餘額:
99.211ETH,智能合約運行已消耗

第二個錢包帳戶的餘額:

100ETH,未消耗過

至此MetaMask的安裝,配置已經完成。

6. 領養寵物

修改.\dapp-guide-pet-shop\src\index.html文件引用jquery的地址

<!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <!-- <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> --> <script src="http://libs.baidu.com/jquery/2.1.1/jquery.min.js"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> 複製代碼

在WINDOWS瀏覽器輸入測試網站地址 http://192.168.80.144:3000/ 能夠看到寵物商店的入口界面:

可愛的狗狗
點擊「Adopt」按鈕,領養這個最漂亮的狗狗寶貝。MetaMask會提示咱們交易的確認,如圖:
要從當前帳號6扣除交易費用
點擊「SUBMIT」按鈕成功後,這個狗狗的按鈕變爲"SUCCESS",該狗狗已被包養了。
領養成功
查看當前打開的帳號ACCOUNT6,發現金額再也不是100ETH,而是99.999ETH了。
帳戶餘額消耗
恭喜你,開發併成功部署了一個DAPP區塊鏈應用程序,而且領養了幾隻小狗狗。

7. 知識對接服務

咱們在知識星球開通了區塊鏈入門專欄,用於存放本項目的工程源碼等內容,並創建專項微信羣用於技術交流,歡迎加入。

分享碼

8. 參考

1),Truffle官網文檔

2),一步步教你開發、部署第一個Dapp應用:寵物商店

相關文章
相關標籤/搜索