资讯专栏INFORMATION COLUMN

从零构建、部署去中心化Voting Dapp

mikasa / 688人阅读

摘要:查看文件内容这个文件主要是封装了一个的供我们直接使用,可以从直接获取到对象供文件中使用。

如何编写智能合约(Smart Contract)- 从零构建和部署去中心化投票App,decentralization Voting Dapp

</>复制代码

  1. 孔壹学院:国内区块链职业教育领先品牌

  2. 作者:黎跃春,区块链、高可用架构工程师
    微信:liyc1215 QQ群:348924182 博客:http://liyuechun.org

课程目标

了解区块链智能合约

学会搭建智能合约开发环境

学会如何编译智能合约

学会如何将智能合约部署到区块链

学会如何通过WebApp和智能合约尽心互动

掌握DApp(去中心化App)的整个开发部署流程

掌握去中心化在实战产品中应用的重大意义

项目效果图

编辑器选择

理论上讲任何编辑器都可以编写Solidity合约代码,比如:WebStorm,VSCode,Sublime,等等。我选择的是Atom,没有任何理由,因为Atom轻量并且界面漂亮。

移步https://atom.io/地址,下载安装Atom。

autocomplete-solidity代码自动补齐

linter-soliumlinter-solidity代码错误检查

language-ethereum支持Solidity代码高亮以及Solidity代码片段

安装所需工具

首先开发机上必须装好Node.js,再使用以下命令安装所需的工具:

</>复制代码

  1. $ npm install -g ethereumjs-testrpc truffle

</>复制代码

  1. liyuechun:~ yuechunli$ npm install -g ethereumjs-testrpc truffle
  2. /usr/local/bin/testrpc -> /usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js
  3. /usr/local/bin/truffle -> /usr/local/lib/node_modules/truffle/build/cli.bundled.js
  4. + truffle@3.4.9
  5. + ethereumjs-testrpc@4.1.3
  6. added 1 package and updated 7 packages in 76.132s
  7. liyuechun:~ yuechunli$
创建项目

</>复制代码

  1. /Users/liyuechun/Desktop/1012/Voting
  2. liyuechun:Voting yuechunli$ ls
  3. liyuechun:Voting yuechunli$ pwd
  4. /Users/liyuechun/Desktop/1012/Voting
  5. liyuechun:Voting yuechunli$ truffle unbox react-box
  6. Downloading...
  7. Unpacking...
  8. Setting up...
  9. Unbox successful. Sweet!
  10. Commands:
  11. Compile: truffle compile
  12. Migrate: truffle migrate
  13. Test contracts: truffle test
  14. Test dapp: npm test
  15. Run dev server: npm run start
  16. Build for production: npm run build
  17. liyuechun:Voting yuechunli$
项目结构

contracts:编写智能合约的文件夹,所有的智能合约文件都放置在这里

migrations:部署合约配置的文件夹

src:基于React的Web端源码

test:智能合约测试用例文件夹

编写投票Dapp智能合约

contracts文件夹下创建Voting.sol文件,将下面的代码拷贝到文件中。

</>复制代码

  1. pragma solidity ^0.4.4;
  2. contract Voting {
  3. // liyuechun -> 10
  4. // xietingfeng -> 5
  5. // liudehua -> 20
  6. mapping (bytes32 => uint8) public votesReceived;
  7. // 存储候选人名字的数组
  8. bytes32[] public candidateList;
  9. // 构造函数 初始化候选人名单
  10. function Voting(bytes32[] candidateNames) {
  11. candidateList = candidateNames;
  12. }
  13. // 查询某个候选人的总票数
  14. function totalVotesFor(bytes32 candidate) constant returns (uint8) {
  15. require(validCandidate(candidate) == true);
  16. // 或者
  17. // assert(validCandidate(candidate) == true);
  18. return votesReceived[candidate];
  19. }
  20. // 为某个候选人投票
  21. function voteForCandidate(bytes32 candidate) {
  22. assert(validCandidate(candidate) == true);
  23. votesReceived[candidate] += 1;
  24. }
  25. // 检索投票的姓名是不是候选人的名字
  26. function validCandidate(bytes32 candidate) constant returns (bool) {
  27. for(uint i = 0; i < candidateList.length; i++) {
  28. if (candidateList[i] == candidate) {
  29. return true;
  30. }
  31. }
  32. return false;
  33. }
  34. }
通过remix + metamask部署合约到Kovan Test Net

在Google浏览器里面安装MetaMask插件

打开https://remix.ethereum.org将合约代码拷贝到里面

确保MetaMask账号处于等于状态,并且有一定的以太币支付给矿工。

确保EnvironmentInjected Web3,如果切换不过来,关掉浏览器重新启动

create函数中输入一个数组,数组里面的内容为候选人名单

点击create按钮,会弹出MetaMask界面让你确认,确认提交,过一会儿,合约就部署成功

可以测试给某个候选人投票,查询某个候选人的票数

拷贝合约地址

</>复制代码

  1. 0xd3f33a2e553b363b432d7f81f721a2a6202ecc67
编译合约

</>复制代码

  1. liyuechun:Voting yuechunli$ truffle compile
  2. Compiling ./contracts/Migrations.sol...
  3. Compiling ./contracts/SimpleStorage.sol...
  4. Compiling ./contracts/Voting.sol...
  5. Writing artifacts to ./build/contracts
  6. liyuechun:Voting yuechunli$

编译完合约以后在build/contracts文件夹下面会有一个Voting.jsonabi文件。

查看Voting.json文件内容

</>复制代码

  1. {
  2. "contract_name": "Voting",
  3. "abi": [
  4. {
  5. "constant": true,
  6. "inputs": [
  7. {
  8. "name": "candidate",
  9. "type": "bytes32"
  10. }
  11. ],
  12. "name": "totalVotesFor",
  13. "outputs": [
  14. {
  15. "name": "",
  16. "type": "uint8"
  17. }
  18. ],
  19. "payable": false,
  20. "type": "function"
  21. },
  22. {
  23. "constant": true,
  24. "inputs": [
  25. {
  26. "name": "candidate",
  27. "type": "bytes32"
  28. }
  29. ],
  30. "name": "validCandidate",
  31. "outputs": [
  32. {
  33. "name": "",
  34. "type": "bool"
  35. }
  36. ],
  37. "payable": false,
  38. "type": "function"
  39. },
  40. {
  41. "constant": true,
  42. "inputs": [
  43. {
  44. "name": "",
  45. "type": "bytes32"
  46. }
  47. ],
  48. "name": "votesReceived",
  49. "outputs": [
  50. {
  51. "name": "",
  52. "type": "uint8"
  53. }
  54. ],
  55. "payable": false,
  56. "type": "function"
  57. },
  58. {
  59. "constant": true,
  60. "inputs": [
  61. {
  62. "name": "",
  63. "type": "uint256"
  64. }
  65. ],
  66. "name": "candidateList",
  67. "outputs": [
  68. {
  69. "name": "",
  70. "type": "bytes32"
  71. }
  72. ],
  73. "payable": false,
  74. "type": "function"
  75. },
  76. {
  77. "constant": false,
  78. "inputs": [
  79. {
  80. "name": "candidate",
  81. "type": "bytes32"
  82. }
  83. ],
  84. "name": "voteForCandidate",
  85. "outputs": [],
  86. "payable": false,
  87. "type": "function"
  88. },
  89. {
  90. "inputs": [
  91. {
  92. "name": "candidateNames",
  93. "type": "bytes32[]"
  94. }
  95. ],
  96. "payable": false,
  97. "type": "constructor"
  98. }
  99. ],
  100. "unlinked_binary": "0x6060604052341561000f57600080fd5b6040516103113803806103118339810160405280805190910190505b600181805161003e929160200190610046565b505b506100b5565b828054828255906000526020600020908101928215610083579160200282015b828111156100835782518255602090920191600190910190610066565b5b50610090929150610094565b5090565b6100b291905b80821115610090576000815560010161009a565b5090565b90565b61024d806100c46000396000f300606060405263ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416632f265cf78114610069578063392e6678146100955780637021939f146100bf578063b13c744b146100eb578063cc9ab26714610113575b600080fd5b341561007457600080fd5b61007f60043561012b565b60405160ff909116815260200160405180910390f35b34156100a057600080fd5b6100ab60043561015d565b604051901515815260200160405180910390f35b34156100ca57600080fd5b61007f6004356101af565b60405160ff909116815260200160405180910390f35b34156100f657600080fd5b6101016004356101c4565b60405190815260200160405180910390f35b341561011e57600080fd5b6101296004356101e7565b005b60006101368261015d565b151560011461014457600080fd5b5060008181526020819052604090205460ff165b919050565b6000805b6001548110156101a457600180548491908390811061017c57fe5b906000526020600020900160005b5054141561019b57600191506101a9565b5b600101610161565b600091505b50919050565b60006020819052908152604090205460ff1681565b60018054829081106101d257fe5b906000526020600020900160005b5054905081565b6101f08161015d565b15156001146101fb57fe5b6000818152602081905260409020805460ff8082166001011660ff199091161790555b505600a165627a7a723058206783a7ff47eae16f18011a9db2a3cc983350b779bf3181b7623c18d4bce363180029",
  101. "networks": {},
  102. "schema_version": "0.0.5",
  103. "updated_at": 1507806214330
  104. }

这个文件是编译后的abi文件,待会儿需要将这个文件的json导入到App.json中。

查看src/utils/getWeb3.js文件内容

</>复制代码

  1. import Web3 from "web3"
  2. let getWeb3 = new Promise(function(resolve, reject) {
  3. // Wait for loading completion to avoid race conditions with web3 injection timing.
  4. window.addEventListener("load", function() {
  5. var results
  6. var web3 = window.web3
  7. // Checking if Web3 has been injected by the browser (Mist/MetaMask)
  8. if (typeof web3 !== "undefined") {
  9. // Use Mist/MetaMask"s provider.
  10. web3 = new Web3(web3.currentProvider)
  11. results = {
  12. web3: web3
  13. }
  14. console.log("Injected web3 detected.");
  15. resolve(results)
  16. } else {
  17. // Fallback to localhost if no web3 injection.
  18. var provider = new Web3.providers.HttpProvider("http://localhost:8545")
  19. web3 = new Web3(provider)
  20. results = {
  21. web3: web3
  22. }
  23. console.log("No web3 instance injected, using Local web3.");
  24. resolve(results)
  25. }
  26. })
  27. })
  28. export default getWeb3

这个文件主要是封装了一个getWeb3promiss供我们直接使用,可以从getWeb3直接获取到web3对象供App.js文件中使用。

修改app.js前端代码和合约进行互动

</>复制代码

  1. import React, { Component } from "react"
  2. import VotingContract from "../build/contracts/Voting.json"
  3. import getWeb3 from "./utils/getWeb3"
  4. import "./css/oswald.css"
  5. import "./css/open-sans.css"
  6. import "./css/pure-min.css"
  7. import "./App.css"
  8. const contractAddress = "0xd3f33a2e553b363b432d7f81f721a2a6202ecc67";
  9. var votingContractInstance;
  10. var _modifyVotingCount = (candidates,i,votingCount) => {
  11. console.log("---------");
  12. console.log(candidates);
  13. console.log(i);
  14. console.log(votingCount);
  15. let obj = candidates[i];
  16. obj.votingCount = votingCount;
  17. return candidates;
  18. }
  19. class App extends Component {
  20. constructor(props) {
  21. super(props)
  22. this.state = {
  23. candidates: [
  24. {
  25. "name": "Rama",
  26. "id": 100,
  27. "votingCount": 0
  28. },
  29. {
  30. "name": "Nick",
  31. "id": 101,
  32. "votingCount": 0
  33. },
  34. {
  35. "name": "Jose",
  36. "id": 102,
  37. "votingCount": 0
  38. },
  39. {
  40. "name": "liyuechun",
  41. "id": 103,
  42. "votingCount": 0
  43. }
  44. ],
  45. candidatesVoteCount: ["0","0","0","0"],
  46. web3: null
  47. }
  48. }
  49. componentWillMount() {
  50. // Get network provider and web3 instance.
  51. // See utils/getWeb3 for more info.
  52. getWeb3
  53. .then(results => {
  54. this.setState({
  55. web3: results.web3
  56. })
  57. // Instantiate contract once web3 provided.
  58. this.instantiateContract()
  59. })
  60. .catch(() => {
  61. console.log("Error finding web3.")
  62. })
  63. }
  64. instantiateContract() {
  65. /*
  66. * SMART CONTRACT EXAMPLE
  67. *
  68. * Normally these functions would be called in the context of a
  69. * state management library, but for convenience I"ve placed them here.
  70. */
  71. const contract = require("truffle-contract")
  72. const votingContract = contract(VotingContract)
  73. votingContract.setProvider(this.state.web3.currentProvider)
  74. // Declaring this for later so we can chain functions on SimpleStorage.
  75. // Get accounts.
  76. this.state.web3.eth.getAccounts((error, accounts) => {
  77. votingContract.at(contractAddress).then((instance) => {
  78. votingContractInstance = instance;
  79. for (let i = 0; i < this.state.candidates.length; i++) {
  80. let object = this.state.candidates[i];
  81. console.log(accounts[0]);
  82. console.log(votingContractInstance);
  83. console.log(votingContractInstance.totalVotesFor(object.name));
  84. votingContractInstance.totalVotesFor(object.name).then(result => {
  85. console.log(i);
  86. console.log(result.c[0]);
  87. this.setState({
  88. candidates: _modifyVotingCount(this.state.candidates,i,result.c[0])
  89. });
  90. });
  91. }
  92. })
  93. })
  94. }
  95. render() {
  96. return (
    • {
    • this.state.candidates.map((object) => {
    • console.log(object);
    • return (
    • 候选人:{object.name} 支持票数:{object.votingCount}
    • )
    • })
    • }
  97. console.log(this.refs.candidateInput);
  98. console.log(this.refs.candidateInput.value);
  99. let candidateName = this.refs.candidateInput.value;
  100. console.log(this.state.web3.eth.accounts[0]);
  101. votingContractInstance.voteForCandidate(candidateName).then((result => {
  102. console.log(result);
  103. console.log(candidateName);
  104. let number = 0;
  105. for(let i = 0; i < this.state.candidates.length; i++) {
  106. let object = this.state.candidates[i];
  107. if (object.name === candidateName) {
  108. number = i;
  109. break;
  110. }
  111. }
  112. votingContractInstance.totalVotesFor(candidateName).then(result => {
  113. this.setState({
  114. candidates: _modifyVotingCount(this.state.candidates,number,result.c[0])
  115. });
  116. });
  117. }));
  118. }}>Voting
  119. );
  120. }
  121. }
  122. export default App

打赏地址

比特币:1FcbBw62FHBJKTiLGNoguSwkBdVnJQ9NUn
以太坊:0xF055775eBD516e7419ae486C1d50C682d4170645

技术交流

区块链技术交流QQ群:348924182

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/27088.html

相关文章

  • 以太坊智能合约开发第二篇:理解以太坊相关概念

    摘要:原文发表于以太坊智能合约开发第二篇理解以太坊相关概念很多人都说比特币是区块链,以太坊是区块链。它是以太坊智能合约的运行环境。是由以太坊节点提供。以太坊社区把基于智能合约的应用称为去中心化的应用。 原文发表于:以太坊智能合约开发第二篇:理解以太坊相关概念 很多人都说比特币是区块链1.0,以太坊是区块链2.0。在以太坊平台上,可以开发各种各样的去中心化应用,这些应用构成了以太坊的整个生态...

    yibinnn 评论0 收藏0
  • 以太坊智能合约开发第六篇:truffle开发框架

    摘要:原文发表于以太坊智能合约开发第六篇开发框架在前面几篇教程中,我们实现了一个简单的合约,并通过编译器将合约代码编译后,部署在私有链上。 原文发表于:以太坊智能合约开发第六篇:truffle开发框架 在前面几篇教程中,我们实现了一个简单的 Hello 合约,并通过 solc 编译器将合约代码编译后,部署在私有链Ganache上。本篇将介绍通过truffle框架来构建自动编译、部署合约代码...

    ityouknow 评论0 收藏0
  • 以太坊DApp开发入门教程——Node.js和truffle框架打造区块链投票系统

    摘要:第一节课程概述本课程面向初学者,内容涵盖以太坊开发相关的基本概念,并将手把手地教大家如何构建一个基于以太坊的完整去中心化应用区块链投票系统。第七节以太坊世界计算机以太坊是一种区块链的实现。交易数据以太坊中每笔交易都存储在区块链上。 第一节 课程概述 本课程面向初学者,内容涵盖以太坊开发相关的基本概念,并将手把手地教大家如何构建一个 基于以太坊的完整去中心化应用 —— 区块链投票系统。 ...

    zebrayoung 评论0 收藏0
  • 以太坊DApp开发入门教程——Node.js和truffle框架打造区块链投票系统

    摘要:第一节课程概述本课程面向初学者,内容涵盖以太坊开发相关的基本概念,并将手把手地教大家如何构建一个基于以太坊的完整去中心化应用区块链投票系统。第七节以太坊世界计算机以太坊是一种区块链的实现。交易数据以太坊中每笔交易都存储在区块链上。 第一节 课程概述 本课程面向初学者,内容涵盖以太坊开发相关的基本概念,并将手把手地教大家如何构建一个 基于以太坊的完整去中心化应用 —— 区块链投票系统。 ...

    MASAILA 评论0 收藏0
  • 区块链游戏 Dapp 获得思否黑客马拉松北京站冠军

    摘要:上周末,来自硅谷团队的游戏作品获得思否区块链黑客马拉松北京站冠军。本次黑客马拉松有来自基金会科学家杨耀东合伙人刘海核心开发者姜家志星云链实验室负责人刘杜然等多位嘉宾出席。 上周末,来自硅谷团队的游戏作品Crypto Bird - An Implement of NabBoard获得 SegmentFault 思否区块链黑客马拉松北京站冠军。本次黑客马拉松有来自 QuarkChain 基...

    GitChat 评论0 收藏0

发表评论

0条评论

mikasa

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<