基于以太坊的投票应用全栈开发教程

点此注册【火币交易所】享受更低的手续费
比特币价格未来将破10万元,快速买卖比特币  

作者:Mahesh Murthy

翻译:BuGoNee

原文链接:https://medium.com/@mvmurthy/full-stack-hello-world-voting-ethereum-dapp-tutorial-part-1-40d2d0d807c2

本文约10000字+,阅读(观看)需要54分钟

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

作为一名开发者,学习一项新技术的最好方式就是使用该技术构建一项玩具级应用程序。在这篇文章中,我们将会构建一个简单的入门级投票应用程序。

这个应用程序非常简单,它功能包括:初始化一组竞选者,让任何人都能投票给候选人,并将每个候选人的得票数展示出来。我们的目标不仅仅是写出应用程序的代码,还要学习编译、部署、与应用进行交互。

我会尽量避免使用任何dapp编程框架去构建这个应用程序,因为框架将大量的细节进行了抽象,导致你无法理解系统内部的工作机理。而且,不使用框架的另一个好处是等你以后使用框架时,你会更加感谢那些简洁的编程框架在背后为你所做的一切。

本文的练习目的:

  1. 安装开发环境。
  2. 学习如何写一个合约,并编译和部署它到开发环境中。
  3. 通过nodejs的命令行与区块链上的合约交互。
  4. 制作一个简单的网页,来展示投票数量和投票给候选人。

整个应用程序都部署构建在一台新安装的Ubuntu 16.04 Xenial服务器上。经过我的测试,代码也能在mac os系统中正常运行。

这是我们将要构建的应用的整体框架。

配置开发环境

我们将使用一个跑在内存中的区块链(想象一个区块链模拟器),它叫做ganache,而不是在真正的以太坊区块链进行开发。

注意:这篇教程现在使用0.20.2版本的web3js。一旦1.0稳定版发布了,我会更新这篇教程。

请注意ganache会自动创建10个测试账号供使用。这些账号会预充100个(假的)以太币。

一个简单的投票合约

我们将使用solidity编程语言来撰写我们的合约。如果你熟悉面向对象编程,那么学习如何使用solidity来编写合约将会非常容易。我们将写一个名叫Voting的合约(把合约想象成你最喜欢的一门面向对象语言中的类), 它还包含一个构造函数用来初始化一组候选人。

注意:该构造函数在你部署合约到区块链上时被调用且仅会调用一次。不像web世界那样,每次部署代码都会覆盖旧的代码,区块链中部署的代码是不可改变的。即,如果你更新你的合约并再次部署,旧合约和其中存储的数据仍将存在于区块链中,新的部署将会创建新的合约实例。

下面是一份内嵌评论说明的投票合约代码:

pragma solidity ^0.4.18;
// 我们必须指定出所使用的编译器的版本
contract Voting {
    /* 下面的映射字段相当于一个关联数组或散列。
    映射的关键字是候选名称,其存储类型为bytes32,
value是无符号整    型,用来存储投票计数。        */
    mapping (bytes32 => uint8) public votesReceived;
    /* Solidity不允许您传输字符串数组到构造函数中。
    我们取而代之将会使用bytes32类型的数组来存储
    候选人名单。
    */
    bytes32[] public candidateList;
    /* 这部分就是构造函数,它将会在您部署合约到区块链上
        时被调用一次。
    当我们部署合约时,我们会传一组参与竞选的候选人名单
        到构造函数中。
    */
    function Voting(bytes32[] candidateNames) public {
        candidateList = candidateNames;
    }
    // 这个函数返回一位候选者目前的总计得票数
    function totalVotesFor(bytes32 candidate) view
        public returns (uint8) {
        require(validCandidate(candidate));
        return votesReceived[candidate];
    }
    // 这个函数可以增加特定候选者的得票计数,
        相当于投了票。
    function voteForCandidate(bytes32 candidate)
         public {
        require(validCandidate(candidate));
        votesReceived[candidate] += 1;
    }
    // 这个函数检查候选者身份的有效性。
    function validCandidate(bytes32 candidate) view
        public returns (bool) {
        for(uint i = 0; i < candidateList.length; i++) {
            if (candidateList[i] == candidate) {
                return true;
            }
        }
        return false;
    }
}

将上面的代码复制到hello_world_voting目录下名为Voting.sol的文件中。现在让我们编译并部署这段代码到ganache区块链上。

为了编译solidity代码,我们将首先安装名为solc的npm模块

mahesh@projectblockchain:~/
hello_world_voting$ npm install solc

我们将在node控制台内使用这个库来编译我们的合约。记住上一篇文章中我们讲到的,web3js是一个通过RPC通信让您与区块链交互的库。我们将用solc来部署和使用我们的应用程序。

首先,在你的命令行中执行’node’命令来进入node控制台并初始化solc和web3对象。下面的所有的代码片段都需要输入进node控制台。

mahesh@projectblockchain:~/
hello_world_voting$ node
> Web3 = require('web3')
> web3 = new Web3(new Web3.providers.HttpProvider
("http://localhost:8545"));

为了确保web3对象已被初始化并能够与区块链进行通信,我们来查询一下区块链中的所有帐户。您应该看到如下结果:

> web3.eth.accounts
['0x9c02f5c68e02390a3ab81f63341edc1ba5dbb39e',
'0x7d920be073e92a590dc47e4ccea2f28db3f218cc',
'0xf8a9c7c65c4d1c0c21b06c06ee5da80bd8f074a9',
'0x9d8ee8c3d4f8b1e08803da274bdaff80c2204fc6',
'0x26bb5d139aa7bdb1380af0e1e8f98147ef4c406a',
'0x622e557aad13c36459fac83240f25ae91882127c',
'0xbf8b1630d5640e272f33653e83092ce33d302fd2',
'0xe37a3157cb3081ea7a96ba9f9e942c72cf7ad87b',
'0x175dae81345f36775db285d368f0b1d49f61b2f8',
'0xc26bda5f3370bdd46e7c84bdb909aead4d8f35f3']

为了编译合约,从Voting.sol读取代码到一个字符串型变量并编译它。

> code = fs.readFileSync('Voting.sol').toString()
> solc = require('solc')
> compiledCode = solc.compile(code)

当你成功编译代码并打印’合同’对象(只需在node控制台中键入compiledCode以查看内容)时,有两个重要的字段需要你明白:

  1. compiledCode.contracts[‘:Voting’].bytecode:这是Voting.sol中的源代码编译后得到的字节码,也就是将会被部署到区块链上的代码。
  2. compiledCode.contracts[‘:Voting’].interface:这是一个合约的接口或模板(称为abi),它能够告诉合约用户合约中有哪些方法可用。无论何时,只要你想跟合约进行交互,你就需要这份abi定义。您可以在这里阅读更多关于abi的细节(https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI)。

现在让我们来部署合约。首先创建一个contract对象(即下面的VotingContract),它被用来在区块链上部署和初始化合约。

 > abiDefinition = JSON.parse
 (compiledCode.contracts[':Voting'].interface)
 > VotingContract = web3.eth.contract(abiDefinition)
 > 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)

上面的VotingContract.new将合同部署到区块链。第一个参数是一个存有参与竞选的候选人的数组。让我们来看看第二个参数中散列中的全部内容:

  1. data: 这是经过编译的字节码,我们将要部署到区块链上。
  2. from: 区块链必须可以追踪合约的部署者。在这个例子中,我们选择web3.eth.accounts所返回的账号中的第一个账号作为该合约的拥有者(这个账号将会部署合约到区块链上)。请记住,当我们启动测试区块链时,web3.eth.accounts会返回一组10个ganache创建的测试账号。在真实的区块链中,你是不能随意使用任何帐户的。你必须拥有一个账户并在交易之前将其解锁。在创建帐户时要求你输入密码,这是你拥有该帐户所有权的凭证。为了方便,Ganache默认解锁所有10个帐户。
  3. gas: 与区块链交互是要花费金钱的。这些钱会支付给矿工,因为他们做了将你的代码收录入区块链的全部工作。你必须指定你愿意支付多少钱来将你的代码收录入区块链中,并通过设置”gas“值来实现这一点。您的”来源“账户中的以太币将被用于购买gas。gas的价格由网络决定。

我们已经部署了合约,并有一个合约实例(即上面的contractInstance变量),现在我们可以用它来与合约进行交互了。目前已经有成百上千个合约部署在了区块链上。那么,如何在区块链中识别你的合约?答案是:deployedContract.address。当你与你的合约进行交互时,你需要我们之前讨论过的部署地址和abi定义。

在nodejs控制台中与合约交互

> contractInstance.totalVotesFor.call('Rama') { [String: '0'] s: 1, e: 0, c: [ 0 ] } > contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]}) '0xdedc7ae544c3dde74ab5a0b07422c5a51b5240603d31074f5b75c0ebc786bf53' > contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]}) '0x02c054d238038d68b65d55770fabfca592a5cf6590229ab91bbe7cd72da46de9' > contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]}) '0x3da069a09577514f2baaa11bc3015a16edf26aad28dffbcd126bde2e71f2b76f' > contractInstance.totalVotesFor.call('Rama').toLocaleString() '3'

在node控制台中尝试上述命令,你应该会看到投票计数增加。每次当你为候选人投票时,你都会得到一个交易ID:如上面的‘0xdedc7ae544c3dde74ab5a0b07422c5a51b5240603d31074f5b75c0ebc786bf53’。这个交易是不可撤销的。这种不可撤销的性质是以太坊等区块链的一大优势。在将来的教程中,我们将构建利用这种不可撤销性质的应用程序。

将网页连接到区块链和并实现投票

现在大部分工作都已经完成了,现在我们所要做的是创建一个包含候选者名称的简单html文件,并在js文件中调用投票命令(我们已经在nodejs控制台中尝试和测试过的)。在下面您可以找到html代码和js文件。将它们都放在hello_world_voting目录中,然后在浏览器中打开index.html。

如果你还记得,我们早些时候说过,我们与任何合约交互都需要abi和地址。那么你可以在index.js文件中看到如何使用它们与合约进行交互。

下图是你在浏览器中打开index.html所能够看到的内容

如果你能够在文本框中输入候选人姓名并进行投票且看到投票数增加,那么你就成功创建了你的第一个应用程序!恭喜你!我们总结一下,通过本教程你学会了设置开发环境,编写简单的合约,编译和部署合约到区块链上,还能通过nodejs控制台和网页与合约交互。现在您可以停下来松口气了:)

在第二部分中,我们将会部署合约到公共测试网络中,以便全世界都可以看到它并投票给候选人。我们也将变得更加熟练,并使用truffle框架来进行开发(而不必是使用nodejs控制台来管理整个过程)。希望本教程能够帮助你深入了解如何开始在以太坊平台上开发去中心化应用程序。

文章发布只为分享区块链技术内容,版权归原作者所有,观点仅代表作者本人,绝不代表区块链兄弟赞同其观点或证实其描述。

本文由 区块链技术网 作者:作者:Mahesh Murthy 发表,其版权均为 区块链技术网 所有,文章内容系作者个人观点,不代表 区块链技术网 对观点赞同或支持。如需转载,请注明文章来源。

发表评论