实战-动手开发第一个区块链投票DAPP

转载自https://www.imooc.com/article/25330


实战-动手开发第一个区块链投票DAPP_第1张图片

前言
  • 我是一个菜鸟,所以在代码质量上可能不太好,欢迎指点。
  • 阅读本文可能需要一定的基础,有疑问欢迎留言。

本篇文章,将带读者用Truffle框架在ganache环境上
实战-动手开发第一个区块链投票DAPP_第2张图片

搭建一个属于自己的投票DAPP雏形,你可以在这基础上进行扩展。这里如果你对ganache不熟悉的可以使用testrpc环境也是一样的。

开发包对应版本
  • web3.js v0.20.5
  • Truffle v4.1.3
  • Solidity v0.4.2
  • Ganache 1.1.0
初始化工程

这里我们需要提前安装好 node 和 Truffle,如果不会的,可自行翻阅文档,网上很多资料。

  • 新建一个文件夹
  • 在新建的文件下执行下面的命令
    truffle unbox pet-shop

    这个命令是通过 truffle 的 unbox 工具初始化一个宠物DAPP,我们这里偷个懒,在官方的 pet-shop 项目上进行修改。

编写合约

contracts文件夹下面新建一个 Election.sol 的合约文件,在这里我们进行合约编写。

合约内容如下:

pragma solidity ^0.4.2;

contract Election {

    //结构体
    struct Candidate {
        uint id;
        string name;
        uint voteCount;
    }

    //事件
    event votedEvent(
        uint indexed _candidateId
    );

    //存储结构体
    mapping (uint => Candidate) public candidates;
    //是否已经投票了
    mapping (address=>bool) public voters;

    //总数量
    uint public candidateCount;

    //构造函数
    function Election () public {
        addCandidate("张三");
        addCandidate("李四");
    }

    //添加候选人
    function addCandidate(string _name) private {
        candidateCount ++;
        candidates[candidateCount] = Candidate(candidateCount, _name, 0);
    }

    //投票
    function vote(uint _candidateId) public {

        //过滤
        require(!voters[msg.sender]);
        require(_candidateId > 0 && _candidateId <= candidateCount);

        //记录用户已经投票了
        voters[msg.sender] = true;
        candidates[_candidateId].voteCount ++;

        votedEvent(_candidateId);
    }

}

合约部分不赘述,如果有疑问可以留言。

部署合约

Truffle框架部署合约很简单,可以通过他的 migration 功能直接部署。

  • 在 migrations 文件夹下面,新建一个名为2_deploy_contract.js 的文件。
  • 在里面我们把我们的合约给加到 migration 里面,代码如下:
    
    var Election = artifacts.require("./Election.sol");

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

> 这里说明下,如果你想偷懒,也可以直接在他下面的`1_initial_migration.js`文件里面导入`Election.sol`文件再加到 `migration` 里面。

- 在一切准备就绪,只需要在终端上执行下面的命令

truffle migrate --reset

就能部署好了。为啥要加 `--reset` 如果你的合约第一次部署,可以不加,如果是迭代覆盖部署,就的加这个参数,好从新给你分配一个地址,否者会出现莫名其妙的问题,具体详细介绍,请查阅官方文档。

> 如果你这里使用的tesrpc环境,服务地址的端口是 `8545` 你需要去 修改下项目里面的 `truffle.js`文件里面的配置
![](https://upload-images.jianshu.io/upload_images/2275747-6919f8c10773060c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

## 编写html页面

由于我们是在 `pet-shop` 项目上进行修改的,所以我们只需要去修改 `src/index.html` 文件的内容就可以了。

我们把内容修改成如下内容:

```html


  
    
    
    
    区块链投票
    

  
  
    

区块链投票



Loading...


代码很简单,不用解释应该都能看懂哈。

编写js文件

js部分是DAPP比较麻烦的地方,也是最初学者迷惑的地方,我这里先把最终代码粘贴过来再解释:

App = {
  web3Provider: null,
  contracts: {},
  account: '0x0',

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

  initWeb3: function() {
    if (typeof web3 !== 'undefined') {
      App.web3Provider = web3.currentProvider;
      console.warn("Meata");
    }else{
      App.web3Provider = new Web3.providers.HttpProvider('https://localhost:7545');
    }
    web3 = new Web3(App.web3Provider);

    return App.initContract();
  },

  initContract: function() {

    $.getJSON("Election.json",function(election){

      App.contracts.Election = TruffleContract(election);
      App.contracts.Election.setProvider(App.web3Provider);

      App.listenForEvents();

      return App.reander();
    })

  },

  reander: function(){

    var electionInstance;
    var $loader = $("#loader");
    var $content = $("#content");

    $loader.show();
    $content.hide();

    //获得账号信息
    web3.eth.getCoinbase(function(err,account){
      if(err === null){
        App.account = account;
        $("#accountAddress").html("您当前的账号: " + account);
      }
    });

    //加载数据
    App.contracts.Election.deployed().then(function(instance){
      electionInstance = instance;
      return electionInstance.candidateCount();
    }).then(function(candidatesCount){
      var $candidatesResults = $("#candidatesResults");
      $candidatesResults.empty();

      var $cadidatesSelect = $("#cadidatesSelect");
      $cadidatesSelect.empty();

      for (var i=1;i<=candidatesCount;i++){
        electionInstance.candidates(i).then(function(candidate){
          var id = candidate[0];
          var name = candidate[1];
          var voteCount = candidate[2];

          var candidateTemplate = ""+id+""+name+""+voteCount+"";
          $candidatesResults.append(candidateTemplate);

          //投票
          var cadidateOption = "+name+"";
          $cadidatesSelect.append(cadidateOption);

        });
      }

      return electionInstance.voters(App.account);

    }).then(function(hasVoted){

      if(hasVoted){
        $('form').hide();
      }
      $loader.hide();
      $content.show();

    }).catch(function(err){
      console.warn(err);
    });

  },

  //投票
  castVote: function(){

    var $loader = $("#loader");
    var $content = $("#content");

    var candidateId = $('#cadidatesSelect').val();

    App.contracts.Election.deployed().then(function(instance){
      return instance.vote(candidateId,{from: App.account});
    }).then(function(result){
      $content.hide();
      $loader.show();
    }).catch(function(err){
      console.warn(err);
    });

  },

  //监听事件
  listenForEvents: function(){
    App.contracts.Election.deployed().then(function(instance){
      instance.votedEvent({},{
       formBlock:0,
       toBlock: 'latest'
      }).watch(function(error,event){
        console.log("event triggered",event);
        App.reander();
      });
    })
  }

};

$(function() {
  $(window).load(function() {
    App.init();
  });
});
  • initWeb3方法里面主要是对web3.js进行初始化,应该都能看懂。
  • initContract方法中
    • getJSON 方法是从本地读取json文件,在json文件读取成功后,再调用 Truffle 的 TruffleContract 方法进行合约初始化。
    • 初始化合约后,通过 setProvider 方法我这里理解是设置代理。

其他的都是调取的web3.js提供的api,除了api之外我觉得最有必要解释的是 App.contracts.Election.deployed().then(function(instance)... 这一串代码,这是实例化Election合约后会调取后面then 里面的方法同时,把实例化的变量通过 instance 带入到方法的参数里面。

同时在then里面有返回了一个方法 return instance.vote(candidateId,{from: App.account}); 这个方法又会执行,执行完后,又把执行的结果待会给下一个 then ,依次类推,这貌似是es6的链式语法。

如果我解释得不太明白,可以留言。

运行起来

上面的代码啥的一切准备就绪,现在只需要执行

npm run dev

项目就启动了,由于需要和testrpc或者Ganache交互,所有我们需要用到 MetaMask 插件,所以得要用谷歌浏览器,打开我们的项目,同时需要MetaMask 插件连接到我们的测试环境。
实战-动手开发第一个区块链投票DAPP_第3张图片
就能看到出来的效果了。

如果对MetaMask不了解的,可以在网上查阅相关资料。这里我的理解是,在DAPP中MetaMask充当的是一个桥梁作用,当我我们需要用到签名时,他会出现一个签名的界面让你确认,如下图:

实战-动手开发第一个区块链投票DAPP_第4张图片

其实就是一个轻钱包。


作者: 谦益 
链接:https://www.imooc.com/article/25330
来源:慕课网
本文原创发布于慕课网 ,转载请注明出处,谢谢合作

你可能感兴趣的:(以太坊,区块链)