如何从零开始搭建一个Truffle框架的DAPP应用

如何从零开始搭建一个Truffle框架的DAPP应用

## 1,摘要 [技术小白如何开发一个DAPP区块链应用(以宠物商店为例)](https://www.jianshu.com/p/9844d97ef1d0) 介绍了如何获取宠物商店的TRUFLLE框架代码,并完成部署的过程。 但是这个是已经成熟的代码框架,一般用户要开发自己的项目。那如何借用宠物商店成熟框架完成自有DAPP的搭建呢?我们以tiny熊老师的一个姓名/年龄智能合约用例来呈现方法。 ## 2, 需求描述 我们要实现一个用户姓名和年纪的输入和呈现页面,能更新智能合约上的用户名和年龄。重新输入用户名和年纪,点击按钮可更新智能合约的这2个变量信息。 ## 3,操作步骤 ### 3.1 创建目录,下载框架 首先创建好目录,下载宠物商店的代码框架。 ``` duncanwang@ubuntu:~/work$ mkdir name-age duncanwang@ubuntu:~/work$ cd name-age duncanwang@ubuntu:~/work/name-age$ 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 duncanwang@ubuntu:~/work/name-age$ ``` ### 3.2 创建智能合约代码 新建一个InfoContract.sol智能合约文件,并把它更新到./contracts目录下。 ``` pragma solidity ^0.4.24; contract InfoContract { string name; uint age; event Instructor(string name, uint age); function setInfo(string _name, uint _age) public { name = _name; age = _age; emit Instructor(name, age); } function getInfo() public view returns(string, uint) { return (name, age); } } ``` ### 3.3 增加合约相关的部署和测试代码 1) 增加合约部署测试 文件2_info_contract.js到./migrations目录,代码如下,表示contract InfoContract合约部署。 ``` var MyContract = artifacts.require("./InfoContract.sol"); module.exports = function(deployer) { // deployment steps deployer.deploy(MyContract); }; ``` 2) 增加测试文件 ``` pragma solidity ^0.4.24; import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../contracts/InfoContract.sol"; contract TestInfoContract { InfoContract info = InfoContract(DeployedAddresses.InfoContract()); string name; uint age; function testInfo() { info.setInfo("ABC", 10); (name, age) = info.getInfo(); Assert.equal(name, "ABC", "设置名字出错"); Assert.equal(age, 10, "设置年龄出错"); } } ``` 1. 修改配置文件 因为默认ganache-cli的端口为8545,所以需要修改truffle.js的端口号由7545 变为8545。 ``` 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 } } }; ``` 否则测试时会有找不到客户端提示。 ``` duncanwang@ubuntu:~/work/name-age$ truffle test Could not connect to your Ethereum client. Please check that your Ethereum client: - is running - is accepting RPC connections (i.e., "--rpc" option is used in geth) - is accessible over the network - is properly configured in your Truffle configuration file (truffle.js) ``` ### 3.4 验收测试智能合约 1)参考宠物商店的文章代码,在一个窗口启动一个ganache-cli 钱包。 ``` duncanwang@ubuntu:~/work/name-age$ cd .. duncanwang@ubuntu:~/work$ ganache-cli >>trace.log ``` 2)编译智能合约 然后启动另外一个窗口命令行,输入一下命令。 ``` duncanwang@ubuntu:~/work/name-age$ truffle compile Compiling ./contracts/InfoContract.sol... Compiling ./contracts/Migrations.sol... Writing artifacts to ./build/contracts ``` 智能合约验收命令。 测试成功的提示说明: ``` duncanwang@ubuntu:~/work/name-age$ truffle test Using network 'development'. Compiling ./contracts/InfoContract.sol... Compiling ./test/TestInfoContract.sol... Compiling truffle/Assert.sol... Compiling truffle/DeployedAddresses.sol... Compilation warnings encountered: /home/duncanwang/work/name-age/test/TestInfoContract.sol:12:4: Warning: No visibility specified. Defaulting to "public". function testInfo() { ^ (Relevant source part starts here and spans across multiple lines). TestInfoContract testInfo (838ms) 1 passing (5s) ``` ### 3.5 完成前端页面 完成以下2个文件的修改更新和上传。 1) index.html 把宠物商店的index.html的代码删除,替换为本文需要的框架代码。 ``` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>First Truffle DApp Demo</title> <link rel="stylesheet" type="text/css" href="main.css"> </head> <body> <div class="container"> <h1> First Truffle DApp Demo</h1> <h2 id="info"></h2> <img id="loader" src="https://loading.io/spinners/double-ring/lg.double-ring-spinner.gif"> <label for="name" class="col-lg-2 control-label">姓名:</label> <input id="name" type="text"> <label for="name" class="col-lg-2 control-label">年龄:</label> <input id="age" type="text"> <button id="button">更新</button> </div> <script src="http://libs.baidu.com/jquery/2.1.1/jquery.min.js"></script> <script src="js/web3.min.js"></script> <script src="js/truffle-contract.js"></script> <script src="js/app.js"></script> ``` 2) app.js 然后修改app.js的代码,完成智能合约的执行和调用作用。 ``` App = { web3Provider: null, contracts: {}, init: function() { return App.initWeb3(); }, /*加载web3*/ initWeb3: function() { if (typeof web3 !== 'undefined') { App.web3Provider = web3.currentProvider web3 = new Web3(App.web3Provider); } else { App.web3Provider = new Web3.providers.HttpProvider("http://localhost:9545") web3 = new Web3(App.web3Provider); } return App.initContract(); }, /*初始化合约,获取合约,不需要使用at()的方式; 显示合约的姓名和年龄信息*/ initContract: function() { $.getJSON('InfoContract.json', function(data){ App.contracts.InfoContract = TruffleContract(data); App.contracts.InfoContract.setProvider(App.web3Provider); App.getInfo(); App.watchChanged(); }); App.bindEvents(); }, getInfo: function() { App.contracts.InfoContract.deployed().then(function(instance) { return instance.getInfo.call(); }).then(function(result) { $("#loader").hide(); $("#info").html(result[0]+' ('+result[1]+' years old)'); console.log(result); }).catch(function(err) { console.error(err); }); }, /*点击按钮更新姓名和年龄,则需要更新到智能合约上*/ bindEvents: function() { $("#button").click(function() { $("#loader").show(); App.contracts.InfoContract.deployed().then(function(instance) { return instance.setInfo($("#name").val(), $("#age").val(), {gas: 500000}); }).then(function(result) { return App.getInfo(); } ).catch(function(err) { console.error(err); }); }); }, watchChanged: function() { App.contracts.InfoContract.deployed().then(function(instance) { var infoEvent = instance.Instructor(); return infoEvent.watch(function(err, result) { $("#loader").hide(); $("#info").html(result.args.name +' ('+ result.args.age +' years old)'); }); }); } } $(function(){ $(window).load(function() { App.init(); }); }); ``` ### 3.6 测试验收前端和合约交互代码 1) 部署合约 合约部署成功。 ``` duncanwang@ubuntu:~/work/name-age$ truffle migrate Using network 'development'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0x5b3cd41a7fa7c58361172ac797412469a10edfbe721d8d81988f19282c9cb6e4 Migrations: 0x92b6ecd23aa98fad36926c12ec701f9aaa0933f4 Saving successful migration to network... ... 0x826fcd5b72b48435bf4f9941305727e52b0b7290631ba7b39f642027b1ee6947 Saving artifacts... Running migration: 2_info_contract.js Deploying InfoContract... ... 0x9943dd7b90207bd9fd1e85524d1d0227f18a92269d73f5a2141cb71c22dda1e9 InfoContract: 0x191391c710e1b632e40b4f2267dbc0f3bdb2bed4 Saving successful migration to network... ... 0x7e11f6e32585524e338e73439e4026c7c766625e5d23d56a4c90f8a11e5001ed Saving artifacts... ``` 2)安装并启动lite-server 1] 安装lite-server 【定义】lite-server 是轻量级的,仅适用于开发 的 node 服务器, 它仅支持 web app。 它能够为你打开浏览器, 当你的html或是JavaScript文件变化时,它会识别到并自动帮你刷新浏览器, 还能使用套接字自动注入变化的CSS, 当路由没有被找到时,它将自动后退页面。 参考 [第一课 如何在WINDOWS环境下搭建以太坊开发环境](https://www.jianshu.com/p/683ea7d62a39) ,完成MetaMask和liteServer的安装。 > duncanwang@ubuntu:~/work/name-age$ npm install lite-server --save-dev 成功安装的输出结果如下: ``` npm WARN pet-shop@1.0.0 No description npm WARN pet-shop@1.0.0 No repository field. npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules/fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"ia32"}) + lite-server@2.4.0 added 342 packages from 273 contributors in 56.82s ``` 2] 在新的窗口完成lite-server的启动。 ``` duncanwang@ubuntu:~/work/name-age$ npm run dev > pet-shop@1.0.0 dev /home/duncanwang/work/name-age > 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://10.225.18.149:3000 -------------------------------------- UI: http://localhost:3001 UI External: http://localhost:3001 -------------------------------------- [Browsersync] Serving files from: ./src [Browsersync] Serving files from: ./build/contracts [Browsersync] Watching files... ``` 1. 打开主页 输入lite-server提示的主页地址:http://10.225.18.149:3000 可以看到页面输出信息。 ![](https://img.learnblockchain.cn/2020/02/01_/130907097.png) 4)更新姓名和年龄 输入框输入姓名和年龄:王登辉,18 ,点击更新按钮,会弹出MEATMASK的交易提示,确认交易。 ![](https://img.learnblockchain.cn/2020/02/01_/958384380.png) 确认交易后,姓名和年龄信息会更新。 ![](https://img.learnblockchain.cn/2020/02/01_/689502092.png) ## 4,总结 本文仅从操作层面讲解了如何利用宠物商店的模板样例,快速重构一个含前端的DAPP页面。 具体WEB.3J的接口函数及定义,参考文章 [《第十一课 从宠物商店案例看DAPP架构和WEB3.JS交互接口》](https://www.jianshu.com/p/47174718960b) 。 所有工程的源码已上传到知识星球,有需要的同学可加入下载。 ![](https://img.learnblockchain.cn/2020/02/01_/173382118.png)

1,摘要

技术小白如何开发一个DAPP区块链应用(以宠物商店为例) 介绍了如何获取宠物商店的TRUFLLE框架代码,并完成部署的过程。 但是这个是已经成熟的代码框架,一般用户要开发自己的项目。那如何借用宠物商店成熟框架完成自有DAPP的搭建呢?我们以tiny熊老师的一个姓名/年龄智能合约用例来呈现方法。

2, 需求描述

我们要实现一个用户姓名和年纪的输入和呈现页面,能更新智能合约上的用户名和年龄。重新输入用户名和年纪,点击按钮可更新智能合约的这2个变量信息。

3,操作步骤

3.1 创建目录,下载框架

首先创建好目录,下载宠物商店的代码框架。

duncanwang@ubuntu:~/work$ mkdir name-age
duncanwang@ubuntu:~/work$ cd name-age
duncanwang@ubuntu:~/work/name-age$ 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
duncanwang@ubuntu:~/work/name-age$ 

3.2 创建智能合约代码

新建一个InfoContract.sol智能合约文件,并把它更新到./contracts目录下。

pragma solidity ^0.4.24;

contract InfoContract {
    string name;
    uint age;

    event Instructor(string name, uint age);

    function setInfo(string _name, uint _age) public {
        name = _name;
        age = _age;
        emit Instructor(name, age);
    }

    function getInfo() public view returns(string, uint) {
        return (name, age);
    }
}

3.3 增加合约相关的部署和测试代码

1) 增加合约部署测试 文件2_info_contract.js到./migrations目录,代码如下,表示contract InfoContract合约部署。


var MyContract = artifacts.require("./InfoContract.sol");

module.exports = function(deployer) {
  // deployment steps
  deployer.deploy(MyContract);
};

2) 增加测试文件

pragma solidity ^0.4.24;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/InfoContract.sol";

contract TestInfoContract {
   InfoContract info = InfoContract(DeployedAddresses.InfoContract());
   string name;
   uint age;

   function testInfo() {
     info.setInfo("ABC", 10);

     (name, age) = info.getInfo();

     Assert.equal(name, "ABC", "设置名字出错");
     Assert.equal(age, 10, "设置年龄出错");
   }
}
  1. 修改配置文件 因为默认ganache-cli的端口为8545,所以需要修改truffle.js的端口号由7545 变为8545。
module.exports = {
  // See &lt;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
    }
  }
};

否则测试时会有找不到客户端提示。

duncanwang@ubuntu:~/work/name-age$ truffle test
Could not connect to your Ethereum client. Please check that your Ethereum client:
    - is running
    - is accepting RPC connections (i.e., "--rpc" option is used in geth)
    - is accessible over the network
    - is properly configured in your Truffle configuration file (truffle.js)

3.4 验收测试智能合约

1)参考宠物商店的文章代码,在一个窗口启动一个ganache-cli 钱包。

duncanwang@ubuntu:~/work/name-age$ cd ..
duncanwang@ubuntu:~/work$ ganache-cli >>trace.log

2)编译智能合约 然后启动另外一个窗口命令行,输入一下命令。

duncanwang@ubuntu:~/work/name-age$ truffle compile
Compiling ./contracts/InfoContract.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts

智能合约验收命令。

测试成功的提示说明:

duncanwang@ubuntu:~/work/name-age$ truffle test
Using network 'development'.

Compiling ./contracts/InfoContract.sol...
Compiling ./test/TestInfoContract.sol...
Compiling truffle/Assert.sol...
Compiling truffle/DeployedAddresses.sol...

Compilation warnings encountered:

/home/duncanwang/work/name-age/test/TestInfoContract.sol:12:4: Warning: No visibility specified. Defaulting to "public". 
   function testInfo() {
   ^ (Relevant source part starts here and spans across multiple lines).

  TestInfoContract
     testInfo (838ms)

  1 passing (5s)

3.5 完成前端页面

完成以下2个文件的修改更新和上传。 1) index.html 把宠物商店的index.html的代码删除,替换为本文需要的框架代码。

&lt;!DOCTYPE html>
&lt;html lang="en">
&lt;head>
    &lt;meta charset="UTF-8">
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0">
    &lt;meta http-equiv="X-UA-Compatible" content="ie=edge">
    &lt;title>First Truffle DApp Demo&lt;/title>
    &lt;link rel="stylesheet" type="text/css" href="main.css">

&lt;/head>
&lt;body>
    &lt;div class="container">
        &lt;h1> First Truffle DApp Demo&lt;/h1>

        &lt;h2 id="info">&lt;/h2>
        &lt;img id="loader" src="https://loading.io/spinners/double-ring/lg.double-ring-spinner.gif">

        &lt;label for="name" class="col-lg-2 control-label">姓名:&lt;/label>
        &lt;input id="name" type="text">

        &lt;label for="name" class="col-lg-2 control-label">年龄:&lt;/label>
        &lt;input id="age" type="text">

        &lt;button id="button">更新&lt;/button>

    &lt;/div>

    &lt;script src="http://libs.baidu.com/jquery/2.1.1/jquery.min.js">&lt;/script>
    &lt;script src="js/web3.min.js">&lt;/script>
    &lt;script src="js/truffle-contract.js">&lt;/script>
    &lt;script src="js/app.js">&lt;/script>

2) app.js 然后修改app.js的代码,完成智能合约的执行和调用作用。

App = {
  web3Provider: null,
  contracts: {},

  init: function() {
    return App.initWeb3();
  },

/*加载web3*/
  initWeb3: function() {
    if (typeof web3 !== 'undefined') {
         App.web3Provider = web3.currentProvider
         web3 = new Web3(App.web3Provider);
     } else {
         App.web3Provider = new Web3.providers.HttpProvider("http://localhost:9545")
         web3 = new Web3(App.web3Provider);
     }

     return App.initContract();
  },

/*初始化合约,获取合约,不需要使用at()的方式;
  显示合约的姓名和年龄信息*/
  initContract: function() {

    $.getJSON('InfoContract.json', function(data){
      App.contracts.InfoContract = TruffleContract(data);
      App.contracts.InfoContract.setProvider(App.web3Provider);

      App.getInfo();
      App.watchChanged();
    });

    App.bindEvents();

  },

  getInfo: function() {
    App.contracts.InfoContract.deployed().then(function(instance) {
      return instance.getInfo.call();
    }).then(function(result) {
      $("#loader").hide();
      $("#info").html(result[0]+' ('+result[1]+' years old)');
      console.log(result);
    }).catch(function(err) {
      console.error(err);
    });
  },

/*点击按钮更新姓名和年龄,则需要更新到智能合约上*/
  bindEvents: function() {
    $("#button").click(function() {
        $("#loader").show();

        App.contracts.InfoContract.deployed().then(function(instance) {
          return instance.setInfo($("#name").val(), $("#age").val(), {gas: 500000});
        }).then(function(result) {
          return App.getInfo();
        } ).catch(function(err) {
          console.error(err);
        });
      });
  },

  watchChanged: function() {
    App.contracts.InfoContract.deployed().then(function(instance) {
      var infoEvent = instance.Instructor();
      return infoEvent.watch(function(err, result) {
        $("#loader").hide();
        $("#info").html(result.args.name +' ('+ result.args.age +' years old)');
      });
    });
  }

  }

$(function(){
  $(window).load(function() {
      App.init();
  });
});

3.6 测试验收前端和合约交互代码

1) 部署合约 合约部署成功。

duncanwang@ubuntu:~/work/name-age$ truffle migrate
Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x5b3cd41a7fa7c58361172ac797412469a10edfbe721d8d81988f19282c9cb6e4
  Migrations: 0x92b6ecd23aa98fad36926c12ec701f9aaa0933f4
Saving successful migration to network...
  ... 0x826fcd5b72b48435bf4f9941305727e52b0b7290631ba7b39f642027b1ee6947
Saving artifacts...
Running migration: 2_info_contract.js
  Deploying InfoContract...
  ... 0x9943dd7b90207bd9fd1e85524d1d0227f18a92269d73f5a2141cb71c22dda1e9
  InfoContract: 0x191391c710e1b632e40b4f2267dbc0f3bdb2bed4
Saving successful migration to network...
  ... 0x7e11f6e32585524e338e73439e4026c7c766625e5d23d56a4c90f8a11e5001ed
Saving artifacts...

2)安装并启动lite-server 1] 安装lite-server 【定义】lite-server 是轻量级的,仅适用于开发 的 node 服务器, 它仅支持 web app。 它能够为你打开浏览器, 当你的html或是JavaScript文件变化时,它会识别到并自动帮你刷新浏览器, 还能使用套接字自动注入变化的CSS, 当路由没有被找到时,它将自动后退页面。 参考 第一课 如何在WINDOWS环境下搭建以太坊开发环境 ,完成MetaMask和liteServer的安装。

duncanwang@ubuntu:~/work/name-age$ npm install lite-server --save-dev

成功安装的输出结果如下:

npm WARN pet-shop@1.0.0 No description
npm WARN pet-shop@1.0.0 No repository field.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"ia32"})

+ lite-server@2.4.0
added 342 packages from 273 contributors in 56.82s

2] 在新的窗口完成lite-server的启动。

duncanwang@ubuntu:~/work/name-age$ npm run dev

> pet-shop@1.0.0 dev /home/duncanwang/work/name-age
> 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://10.225.18.149:3000
 --------------------------------------
          UI: http://localhost:3001
 UI External: http://localhost:3001
 --------------------------------------
[Browsersync] Serving files from: ./src
[Browsersync] Serving files from: ./build/contracts
[Browsersync] Watching files...
  1. 打开主页 输入lite-server提示的主页地址:http://10.225.18.149:3000 可以看到页面输出信息。

4)更新姓名和年龄 输入框输入姓名和年龄:王登辉,18 ,点击更新按钮,会弹出MEATMASK的交易提示,确认交易。

确认交易后,姓名和年龄信息会更新。

4,总结

本文仅从操作层面讲解了如何利用宠物商店的模板样例,快速重构一个含前端的DAPP页面。 具体WEB.3J的接口函数及定义,参考文章 《第十一课 从宠物商店案例看DAPP架构和WEB3.JS交互接口》 。 所有工程的源码已上传到知识星球,有需要的同学可加入下载。

  • 发表于 2018-10-10 17:29
  • 阅读 ( 3012 )
  • 学分 ( 5 )
  • 分类:以太坊

评论