以太坊开发:如何开发一个简易Dapp

in #cn7 years ago


我们这里写一个简单的投票智能合约,可以通过Dapp对给定的候选人投票并计算每个候选人获得的票数。


一、环境配置

安装nodejs,npm,git,web3,solc,testrpc等等


二、运行testrpc

先运行testrpc(会自动生成十个账户,并且每个账户中都会初始有100个以太币)


三、用Solidity编写智能合约并使用solc编译


先写一个投票智能合约,这个合约有四个方法,分别是构造方法,查询票数,投票,和判断是否是候选人,逻辑非常简单,甚至不需要懂得Solidity语法都可以看得懂。


Voting.sol 代码:


pragma solidity ^0.4.11;

// We have to specify what version of compiler this code will compile with


contract Voting {

  /* mapping field below is equivalent to an associative array or hash.

  The key of the mapping is candidate name stored as type bytes32 and value is

  an unsigned integer to store the vote count

  */

  

  mapping (bytes32 => uint8) public votesReceived;

  

  /* Solidity doesn't let you pass in an array of strings in the constructor (yet).

  We will use an array of bytes32 instead to store the list of candidates

  */

  

  bytes32[] public candidateList;


  /* This is the constructor which will be called once when you

  deploy the contract to the blockchain. When we deploy the contract,

  we will pass an array of candidates who will be contesting in the election

  */

  function Voting(bytes32[] candidateNames) {

    candidateList = candidateNames;

  }


  // This function returns the total votes a candidate has received so far

  function totalVotesFor(bytes32 candidate) returns (uint8) {

    if (validCandidate(candidate) == false) throw;

    return votesReceived[candidate];

  }


  // This function increments the vote count for the specified candidate. This

  // is equivalent to casting a vote

  function voteForCandidate(bytes32 candidate) {

    if (validCandidate(candidate) == false) throw;

    votesReceived[candidate] += 1;

  }


  function validCandidate(bytes32 candidate) returns (bool) {

    for(uint i = 0; i < candidateList.length; i++) {

      if (candidateList[i] == candidate) {

        return true;

      }

    }

    return false;

  }

}


然后新开窗口进入node,然后输入以下语句:


先初始化一个web3对象,并可以利用这个web3对象来和区块链进行交互。比如,可以通过这个web3对象来查询它所连接到的区块链的账户信息

> Web3 = require('web3')

> web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));


查看当前的账号(testrpc已生成)

> web3.eth.accounts


编译上面的智能合约代码并存到compiledCode当中

> solc = require('solc')

> code = fs.readFileSync('Voting.sol').toString()

> compiledCode = solc.compile(code)


四、部署智能合约


abiDefinition中保存的是该智能合约的界面信息,JSON.parse() 方法解析一个JSON字符串,构造由字符串描述的JavaScript值或对象。也就是说,把JSON字符串解析为JavaScript值。

> abiDefinition = JSON.parse(compiledCode.contracts[':Voting'].interface)


初始化一个投票合约对象

> VotingContract = web3.eth.contract(abiDefinition)


将这些字节码存到byteCode这个变量中去

> byteCode = compiledCode.contracts[':Voting'].bytecode


将这个合约部署到以太链

> deployedContract = VotingContract.new(['Rama','Nick','Jose'],{data: byteCode, from: web3.eth.accounts[0], gas: 4700000})


获取这个合约的地址

> deployedContract.address

> contractInstance = VotingContract.at(deployedContract.address)


五、在控制台中与智能合约进行交互

> contractInstance.totalVotesFor.call('Rama')

> contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]})

> contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]})

> contractInstance.totalVotesFor.call('Rama').toLocaleString()


六、利用网页与智能合约进行交互


在上一步的交互中我们是在nodejs中进行投票和查询的,现在我们就要把这些命令写到js中,并写一个简单的html文件,通过网页来与智能合约进行交互。html和js文件见下。把这两个文件放到与Voting.sol同级别的目录下。


index.html


<!DOCTYPE html>

<html>

<head>

  <title>Hello World DApp</title>

  <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'>

  <link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' rel='stylesheet' type='text/css'>

</head>

<body class="container">

  <h1>SSC VOTING APPLICATION</h1>

  <div class="table-responsive">

    <table class="table table-bordered">

      <thead>

        <tr>

          <th>Candidate</th>

          <th>Votes</th>

        </tr>

      </thead>

      <tbody>

        <tr>

          <td>Rama</td>

          <td id="candidate-1"></td>

        </tr>

        <tr>

          <td>Nick</td>

          <td id="candidate-2"></td>

        </tr>

        <tr>

          <td>Jose</td>

          <td id="candidate-3"></td>

        </tr>

      </tbody>

    </table>

  </div>

  <input type="text" id="candidate" />

  <a href="#" onclick="voteForCandidate()" class="btn btn-primary">Vote</a>

</body>

<script src="https://cdn.rawgit.com/ethereum/web3.js/develop/dist/web3.js"></script>

<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>

<script src="./index.js"></script>

</html>


index.js


web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

abi = JSON.parse('[{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"totalVotesFor","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"validCandidate","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"votesReceived","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"x","type":"bytes32"}],"name":"bytes32ToString","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"candidateList","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"voteForCandidate","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"contractOwner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"inputs":[{"name":"candidateNames","type":"bytes32[]"}],"payable":false,"type":"constructor"}]')

VotingContract = web3.eth.contract(abi);

//在你的控制台中, 执行contractInstance.address,并将获得的地址替换下面这个0x413a...地址

contractInstance = VotingContract.at('0x4131a0f92d36932d3ec3b7a0581546f2e662ad0b');

candidates = {"Rama": "candidate-1", "Nick": "candidate-2", "Jose": "candidate-3"}


function voteForCandidate(candidate) {

  candidateName = $("#candidate").val();

  contractInstance.voteForCandidate(candidateName, {from: web3.eth.accounts[0]}, function() {

    let div_id = candidates[candidateName];

    $("#" + div_id).html(contractInstance.totalVotesFor.call(candidateName).toString());

  });

}


$(document).ready(function() {

  candidateNames = Object.keys(candidates);

  for (var i = 0; i < candidateNames.length; i++) {

    let name = candidateNames[i];

    let val = contractInstance.totalVotesFor.call(name).toString()

    $("#" + candidates[name]).html(val);

  }

});


在index.js中把合约的地址替换之后打开index.html即可在网页上进行我们的投票操作,同时可以通过MetaMask看到我们的交易和以太币的变动情况。

Sort:  

@fblife, 这个不错,赞了!

steemit上代码看起来好难受