(2022年最新版本)Linux下基于ganache(v6.12.2)的简单投票DAPP([email protected])(Node.js: v16.14.0.)(solc:‘0.8.12+commit)

1.下面是基于 Linux 的安装指南。这要求我们预先安装 nodejs 和 npm,再用 npm 安装 ganache-cli、web3 和 solc。

mkdir simple_voting_dapp
cd simple_voting_dapp
npm init
npm install ganache-cli web3 solc
node_modules/.bin/ganache-cli
  • 需保证ganache-cli在整个服务器运行过程中一直运行
  • 如果安装成功,运行命令 node_modules/.bin/ganache-cli,应该能够看到下 图所示的输出。

        为了便于测试,ganache 默认会创建 10 个账户,每个账户有 100 个以太。你需要用其中一个账户创建交易,发送、接收以太。 当然,你也可以安装 GUI 版本的图形化 ganache 而不是命令行版本,在这里下 载 GUI 版本:http://truffleframework.com/ganache/

2.Solidity 合约

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

contract Voting{
    bytes32[] public candidateList;
    mapping(bytes32 => uint8)public voteReceived;
    constructor(bytes32[] memory candidateNames){
        candidateList = candidateNames;
    }
    function totalVotesFor(bytes32 candidateName) public view returns(uint8){
        require(validCandidate(candidateName));
        return voteReceived[candidateName];
    }

    function voteForCandidate(bytes32 candidateName) public {
        require(validCandidate(candidateName));
        voteReceived[candidateName] += 1;
    }

    function validCandidate(bytes32 candidateName) public view returns(bool){
        for(uint8 i = 0; i < candidateList.length; i++){
            if(candidateList[i] == candidateName){
                return true;
            }
        }
        return false;
    }
}

将Voting.sol合约存储到simple_voting_dapp目录下的contract文件夹中

 注意:当你把合约部署到区块链的时候,就会调用构造函数,并只调用一次。与 web 世界里每次部署代码都会覆盖旧代码不同,在区块链上部署的合约是不可 改变的,也就是说,如果你更新合约并再次部署,旧的合约仍然会在区块链上存 在,并且数据仍在。新的部署将会创建合约的一个新的实例

3.编译合约

重新开启一个bash,并进入到simple_voting_dapp/contract中,输入node,在node控制台中操作

> var Web3  = require('web3');
> var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); //ganache-cli 启动的默认连接是 http://localhost:8545
> web3.eth.net.isListening().then(console.log);  //检测连接是否成功
Promise {
  <pending>,
  [Symbol(async_id_symbol)]: 945,
  [Symbol(trigger_async_id_symbol)]: 941,
  [Symbol(destroyed)]: { destroyed: false }
}
> true

> var sourceCode = fs.readFileSync('Voting.sol').toString()
> var solc = require('solc');

> var input = {
  language: 'Solidity',
  sources: {
    'Voting.sol': {
      content: sourceCode
    }
  },
  settings: {
    outputSelection: {
      '*': {
        '*': ['*']
      }
    }
  }
};

> var output = JSON.parse(solc.compile(JSON.stringify(input)));  //编译

4.部署合约 

> var abi = output.contracts['Voting.sol'].Voting['abi'];
> var byteCode = output.contracts['Voting.sol'].Voting['evm']['bytecode'].object;
> var candidatesHex =[web3.utils.toHex('tom'),web3.utils.toHex('jack'),web3.utils.toHex('mary')]; //byte32[]  为Voting.sol构造函数传入参数做准备
> var account;
> web3.eth.getAccounts().then(function(res){account=res[0]});
> var VotingContract = new web3.eth.Contract(abi);
> var contractInstance = VotingContract.deploy({data:byteCode,arguments:[candidatesHex]}).send({from:account,gas: 1500000,gasPrice: '30000000000000'}).then(function(newContractInstance){
    console.log(newContractInstance.options.address) 
});


> 0xC65cD0Ada5fD85f5D200071C98751B474B017680  //获取新合约实例的合约地址

> VotingContract.options.address = '0xC65cD0Ada5fD85f5D200071C98751B474B017680' ; //设置合约地址 不然无法调用合约里面的方法



5.调用合约方法

> VotingContract.methods.voteForCandidate(web3.utils.toHex('tom')).send({from:account}).then(console.log);  //调用Voting.sol中的voteForCandidate方法为tom投票

> VotingContract.methods.totalVotesFor(web3.utils.toHex('tom')).call({from:account}).then(console.log);   //查询tom的投票数

Promise {
  <pending>,
  [Symbol(async_id_symbol)]: 6738,
  [Symbol(trigger_async_id_symbol)]: 6734,
  [Symbol(destroyed)]: { destroyed: false }
}
> 1

6.index.html

<!DOCTYPE html>
<html>
    <head>
        <title>Voting DApp</title>
        <link
            href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/boot
strap.min.css"
            rel="stylesheet"
            type="text/css"
        />
    </head>
    <body class="container">
        <h1>A Simple 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>tom</td>
                        <td id="candidate-1"></td>
                    </tr>
                    <tr>
                        <td>jack</td>
                        <td id="candidate-2"></td>
                    </tr>
                    <tr>
                        <td>mary</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.jsdelivr.net/gh/ethereum/web3.js/dist/web3.mi
n.js"></script>
    <script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>
    <script src="./index.js"></script>
</html>

7.index.js

var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
var abi = JSON.parse('[{"inputs":[{"internalType":"bytes32[]","name":"candidateNames","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"candidateList","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"candidateName","type":"bytes32"}],"name":"totalVotesFor","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"candidateName","type":"bytes32"}],"name":"validCandidate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"candidateName","type":"bytes32"}],"name":"voteForCandidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"voteReceived","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"}]');
var contractAddr = '0x1E3B8A53A32c1C17d16b892c155fAB298e57BBE6';
var contractInstance = new web3.eth.Contract(abi,contractAddr);
var candidates = {'tom':'candidate-1','jack':'candidate-2','mary':'candidate-3'};
var account;
web3.eth.getAccounts().then(function(accounts){
    account = accounts[0];
});

function voteForCandidate(){
    let candidateName = $("#candidate").val();
    let candidateNameHex =  web3.utils.toHex(candidateName);
    contractInstance.methods.voteForCandidate(candidateNameHex).send({from:account}).then(function(receipt){
        $("#msg").html("已投给: "+ candidateName + "<br>交易哈希: " + receipt.transactionHash + "<br>投票人地址: " + account);
        contractInstance.methods.totalVotesFor(candidateNameHex).call(function(err,res){
            $('#'+candidates[candidateName]).html(res);
        });
    });
}

$(document).ready(function(){
    var candidateList = Object.keys(candidates);
    for (let i = 0; i < candidateList.length; i++){
        let name = candidateList[i];
        let nameHex = web3.utils.toHex(name);
        let count = contractInstance.methods.totalVotesFor(nameHex).call(function(err,res){
            $("#"+candidates[name]).html(res);
        });
    }
})

        如果一切顺利的话,你应该能够在文本框中输入候选者姓名,然后投票数应 该加 1 。 注意:由于网络原因,web3.js 可能无法获取,可自行下载到本地导入。 如果你可以看到页面,为候选者投票,然后看到投票数增加,那就已经成功 创建了第一个合约,恭喜!所有投票都会保存到区块链上,并且是不可改变的。 任何人都可以独立验证每个候选者获得了多少投票。当然,我们所有的事情都是在一个模拟的区块链上(ganache)完成。

8.总结

下面是你到目前为止已经完成的事情:

1. 通过安装 node, npm 和 ganache,你已经配置好了开发环境。

2. 你编码了一个简单的投票合约,编译并部署到区块链上。

3. 你通过 nodejs 控制台与网页与合约进行了交互

猜你喜欢

转载自blog.csdn.net/weixin_47450271/article/details/123416668