乐趣区

关于以太坊:以太坊实践整理五DApp开发全过程记录下

阐明:本篇所波及的 DAPP 众筹需要及合约实现来自登链社区

在后面的众筹案例中,创作者没有方法查看到所有参与者,有两个方法能够实现查案所有参与者:

  1. 退出一个状态变量:address[] joinAccounts,这是一个数组,用来记录所有参与者的地址,每当有新的参与者进来时,往数组中退出参与者地址。
  2. 通过触发事件把参与者地址记录到日志中,而后启动一个服务程序监听事件,当事件触发时,把参与者地址记录到数据库中,并提供一个后端服务,把数据库中的参与者列表返回给前端。

两种办法各有优缺点:办法 1 的 gas 耗费会远高于办法 2,长处是不须要额定引入服务器;办法 2 则相同,应用事件的办法 2 其实还有一个益处,就是能够实时监听到事件的变动(通常对应着链上状态的变动),这在一些场合十分有用。

上面次要介绍办法 2,通过后盾服务,监听事件变动。咱们应用 Node.js 及 Express 框架作为后盾服务。

装置服务端环境

进入 crowdfunding 工程目录,创立 server 目录作为后端工程目录,并装置环境:

mkdir server
cd server
npm init
npm install express --save

新建一个文件 index.js,编写服务端程序:

const express = require('express')
const app = express()

app.get('/', (req, res) => res.send('Hello World'))

app.listen(3000, () => console.log('Start Server, listening on port 3000!'))

下面代码,引入了 express 模块,它在后盾常驻运行,并监听 3000 端口,当客户端发动申请后,响应“Hello World”字符串。

启动后端服务:

node index.js

启动后,在浏览器拜访 http://localhost:3000,就能够看到 Hello World!

常驻服务监听合约事件

先批改 Crowdfunding 合约,退出一个事件。而后在 receive 函数中触发该事件:

contract Crowdfunding {
    ...

    // address[] joinAccouts;
    event Join(address indexed user, uint price);

    ...

    // 用户向合约转账时,触发的回调函数
    receive() external payable {
        ...

        emit Join(msg.sender, msg.value);     //  48820  gas
    }

    ...
    
}

批改完合约后,应用 truffle migrate –reset 从新编译部署。

回到后端,在 server 目录下装置 web3:

npm install web3 --save

而后批改 index.js,退出监听 Join 事件的代码:

// express 启动代码
...

// 引入 web 库
var Web3 = require('web3');
// 应用 WebSocket 协定 连贯节点
let web3 = new Web3(new Web3.providers.WebsocketProvider('ws://localhost:7545'));

// 获取合约实例
var Crowdfunding = require('../build/contracts/Crowdfunding.json');
const crowdFund = new web3.eth.Contract(
  Crowdfunding.abi,
  Crowdfunding.networks[5777].address
);

//  监听 Join 事件
crowdFund.events.Join(function(error, event) {if (error) {console.log(error);
  }

  // 打印出交易 hash 及区块号
  console.log("交易 hash:" + event.transactionHash);
  console.log("区块高度:" + event.blockNumber);

  // 取得监听到的数据:console.log("参加地址:" + event.returnValues.user);
  console.log("参加金额:" + event.returnValues.price);

});

在初始化 web3 时,应用了 WebsocketProvider,通过 WebSocket 通信协议与节点通信,如果是应用 Geth 节点,须要应用 –ws 开启服务,开发应用的 Ganache 默认开启了 WebSocket 服务。

重新启动后盾服务:

node index.js

启动前端服务:

npm run serve

在浏览器里拜访 http://localhost:8080,进入前端页面,点击“参加众筹”(如果以后账户参加过,就切换不同账号参加众筹),切换到后端的命令行控制台窗口,能够看到打印出日志:

 交易 hash:0xe45848494c6990469db6de4485708ffb72d2d27bbc64cde92ff67d11394176ad
区块高度:6
参加地址:0xa75a4da940a2dDcE9db7e4FbCC62e236558DbEaA
参加金额:20000000000000000

为了方便管理,咱们能够把这部分业务数据写入到中心化的数据库里。

MySql 数据库环境筹备

装置 Mysql 数据库,并创立以下数据库和表来存储众筹数据:

CREATE DATABASE crowdfund;
use crowdfund;

CREATE TABLE joins(
  id INT UNIQUE AUTO_INCREMENT,
  address VARCHAR(42) UNIQUE,
  price FLOAT NOT NULL,
  tx VARCHAR(66) NOT NULL,
  block_no INT NOT NULL,
  created_at datetime,
  PRIMARY KEY (id)
);

监听数据入库

给后盾装置 node-mysql 驱动:

cd server
npm install mysql --save

批改 index.js,引入 mysql,并退出一个插入数据库函数:

var mysql  = require('mysql');

function getConn() {
  return mysql.createConnection({
    host     : 'localhost',
    user     : 'root',
    password : '123456',
    port     : '3306',
    database : 'crowdfund'
  });
}

function insertJoins(address, price, tx, blockNo) {
  // 连贯数据库
  var connection = getConn();
  
  connection.connect();
  
  // 构建插入语句
  const query = `INSERT into joins (
          address,
          price,
          tx,
          block_no,
          created_at
  ) Values (?,?,?,?,NOW())`;
  const params = [address, price, tx, blockNo];  
  
  // 执行插入操作
  connection.query(query, params, function (error, results) {if (error) throw error;
    // console.log('results=>' + results);
  });
  
  connection.end();}

并在监听打印日志的前面调用 insertJoins() 函数:

//  监听 Join 事件
crowdFund.events.Join(function(error, event) {
  ...

  insertJoins(event.returnValues.user,
    // 把以 wei 为单位的价格转为 ether 单位
    web3.utils.fromWei(event.returnValues.price),
    event.transactionHash,
    event.blockNumber)

});

重启后盾服务:

node index.js

在 DAPP 前端页面单击“参加众筹”后(如果以后账户参加过,就切换不同账号参加众筹),如果一切正常,就能够在数据库查问到相应的众筹记录:

为前端提供众筹记录显示

通过读取数据库的数据,并在 Express 退出一个路由,承受前端申请后返回读取到的数据,在 index.js 先退出一个 getJoins() 函数来读取数据库数据:

// 通过一个回调函数把后果返回进来
function getJoins(callback) {
  // 获取数据库链接
  var connection = getConn();
  connection.connect();

  // 查问 SQL
  const query = `SELECT address, price from joins`;
  const params = [];

  // 查询数据库
  connection.query(query, params, (err, rows)=>{if(err){return callback(err);
    }
    console.log(`result=>`, rows);        
    callback(rows);
  });

  connection.end();}

退出一个新路由 /joins:

app.get('/joins', (req, res) => {
  getJoins( rows=> {
    //  设置容许跨域拜访
    res.set({'Access-Control-Allow-Origin': '*'})
    .send(rows)
  });
})

重启后端服务,浏览器拜访 http://localhost:3000/joins,能够看到返回参加众筹的 JSON 数据:

后端筹备好了后,接下来咱们让前端通过 Ajax 申请拜访 http://localhost:3000/joins 接口获取众筹数据列表。

在前端我的项目根目录下,装置 axios:

npm install axios

批改 CrowdFund.vue,退出获取众筹列表的 getJoins() 函数:

...
import axios from 'axios';

export default {
  name: 'CrowdFund',
  data() {
    return {
      ...
      joinList: [],};
  },

  // 以后 Vue 组件被创立时回调的 hook 函数
  async created() {
    ...
    this.getJoins()},

  methods: {
    ...
    // 获取众筹列表
    getJoins() {axios.get('http://localhost:3000/joins')
        .then(response => {this.joinList = response.data})
        .catch(function (error) { // Ajax 申请失败解决
          console.log(error);
        });
    },

  }
}

最初还须要在 HTML 模板中退出 joinList 的渲染:

<!--  如果是创作者,显示 -->
<div class="box" v-if="isAuthor">
  <div >
    <p v-bind:key="item" v-for="item in joinList" >
      <label> 地址:{{item.address.substring(0, 30) + "..."  }}</label>
      金额:<b> {{item.price}} </b>
    </p>
  </div>

  <button :disabled="closed" @click="withdrawFund"> 提取资金 </button>
</div>

启动前端利用:

npm run serve

浏览器拜访 http://localhost:8000,在 MetaMask 中切换到创作者账号,刷新浏览器后能够看到输入的众筹列表:

在众筹 DAPP 案例中,咱们通过 Vue.js 开发利用前端,应用 Node.js 后端监控合约事件,并将一些外围业务数据用中心化数据库进行了存储。这就囊括了所有在 DAPP 开发中波及的内容了。

退出移动版