何编写智能合约

  • Post author:
  • Post category:其他


去中心化投票App

编辑器选择

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

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

autocomplete-solidity代码自动补齐

autocomplete-solidity

linter-solium、linter-solidity代码错误检查

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

language-ethereum

安装所需工具

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

$ npm install -g ethereumjs-testrpc truffle

liyuechun:~ yuechunli$ npm install -g ethereumjs-testrpc truffle

/usr/local/bin/testrpc -> /usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js

/usr/local/bin/truffle -> /usr/local/lib/node_modules/truffle/build/cli.bundled.js

+ truffle@3.4.9

+ ethereumjs-testrpc@4.1.3

added 1 package and updated 7 packages in 76.132s

liyuechun:~ yuechunli$

创建项目

/Users/liyuechun/Desktop/1012/Voting

liyuechun:Voting yuechunli$ ls

liyuechun:Voting yuechunli$ pwd

/Users/liyuechun/Desktop/1012/Voting

liyuechun:Voting yuechunli$ truffle unbox react-box

Downloading…

Unpacking…

Setting up…

Unbox successful. Sweet!

Commands:

Compile:              truffle compile

Migrate:              truffle migrate

Test contracts:       truffle test

Test dapp:            npm test

Run dev server:       npm run start

Build for production: npm run build

liyuechun:Voting yuechunli$

项目结构

truffle unbox react-box

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

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

src:基于React的Web端源码

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

编写投票Dapp智能合约

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

pragma solidity ^0.4.4;

contract Voting {

// liyuechun -> 10

// xietingfeng -> 5

// liudehua -> 20

mapping (bytes32 => uint8) public votesReceived;

// 存储候选人名字的数组

bytes32[] public candidateList;

// 构造函数 初始化候选人名单

function Voting(bytes32[] candidateNames) {

candidateList = candidateNames;

}

// 查询某个候选人的总票数

function totalVotesFor(bytes32 candidate)  constant returns (uint8) {

require(validCandidate(candidate) == true);

// 或者

// assert(validCandidate(candidate) == true);

return votesReceived[candidate];

}

// 为某个候选人投票

function voteForCandidate(bytes32 candidate) {

assert(validCandidate(candidate) == true);

votesReceived[candidate] += 1;

}

// 检索投票的姓名是不是候选人的名字

function validCandidate(bytes32 candidate) constant returns (bool) {

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

if (candidateList[i] == candidate) {

return true;

}

}

return false;

}

}

通过remix + metamask部署合约到Kovan Test Net

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

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

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

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

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

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

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

拷贝合约地址

0xd3f33a2e553b363b432d7f81f721a2a6202ecc67

编译合约

liyuechun:Voting yuechunli$ truffle compile

Compiling ./contracts/Migrations.sol…

Compiling ./contracts/SimpleStorage.sol…

Compiling ./contracts/Voting.sol…

Writing artifacts to ./build/contracts

liyuechun:Voting yuechunli$

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

查看Voting.json文件内容

{

“contract_name”: “Voting”,

“abi”: [

{

“constant”: true,

“inputs”: [

{

“name”: “candidate”,

“type”: “bytes32”

}

],

“name”: “totalVotesFor”,

“outputs”: [

{

“name”: “”,

“type”: “uint8”

}

],

“payable”: false,

“type”: “function”

},

{

“constant”: true,

“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”: “”,

“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”

},

{

“inputs”: [

{

“name”: “candidateNames”,

“type”: “bytes32[]”

}

],

“payable”: false,

“type”: “constructor”

}

],

“unlinked_binary”: “0x6060604052341561000f57600080fd5b6040516103113803806103118339810160405280805190910190505b600181805161003e929160200190610046565b505b506100b5565b828054828255906000526020600020908101928215610083579160200282015b828111156100835782518255602090920191600190910190610066565b5b50610090929150610094565b5090565b6100b291905b80821115610090576000815560010161009a565b5090565b90565b61024d806100c46000396000f300606060405263ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416632f265cf78114610069578063392e6678146100955780637021939f146100bf578063b13c744b146100eb578063cc9ab26714610113575b600080fd5b341561007457600080fd5b61007f60043561012b565b60405160ff909116815260200160405180910390f35b34156100a057600080fd5b6100ab60043561015d565b604051901515815260200160405180910390f35b34156100ca57600080fd5b61007f6004356101af565b60405160ff909116815260200160405180910390f35b34156100f657600080fd5b6101016004356101c4565b60405190815260200160405180910390f35b341561011e57600080fd5b6101296004356101e7565b005b60006101368261015d565b151560011461014457600080fd5b5060008181526020819052604090205460ff165b919050565b6000805b6001548110156101a457600180548491908390811061017c57fe5b906000526020600020900160005b5054141561019b57600191506101a9565b5b600101610161565b600091505b50919050565b60006020819052908152604090205460ff1681565b60018054829081106101d257fe5b906000526020600020900160005b5054905081565b6101f08161015d565b15156001146101fb57fe5b6000818152602081905260409020805460ff8082166001011660ff199091161790555b505600a165627a7a723058206783a7ff47eae16f18011a9db2a3cc983350b779bf3181b7623c18d4bce363180029”,

“networks”: {},

“schema_version”: “0.0.5”,

“updated_at”: 1507806214330

}

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

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

import Web3 from ‘web3’

let getWeb3 = new Promise(function(resolve, reject) {

// Wait for loading completion to avoid race conditions with web3 injection timing.

window.addEventListener(‘load’, function() {

var results

var web3 = window.web3

// Checking if Web3 has been injected by the browser (Mist/MetaMask)

if (typeof web3 !== ‘undefined’) {

// Use Mist/MetaMask’s provider.

web3 = new Web3(web3.currentProvider)

results = {

web3: web3

}

console.log(‘Injected web3 detected.’);

resolve(results)

} else {

// Fallback to localhost if no web3 injection.

var provider = new Web3.providers.HttpProvider(‘http://localhost:8545’)

web3 = new Web3(provider)

results = {

web3: web3

}

console.log(‘No web3 instance injected, using Local web3.’);

resolve(results)

}

})

})

export default getWeb3

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

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

import React, { Component } from ‘react’

import VotingContract from ‘../build/contracts/Voting.json’

import getWeb3 from ‘./utils/getWeb3’

import ‘./css/oswald.css’

import ‘./css/open-sans.css’

import ‘./css/pure-min.css’

import ‘./App.css’

const contractAddress = “0xd3f33a2e553b363b432d7f81f721a2a6202ecc67”;

var votingContractInstance;

var _modifyVotingCount = (candidates,i,votingCount) => {

console.log(“———“);

console.log(candidates);

console.log(i);

console.log(votingCount);

let obj = candidates[i];

obj.votingCount = votingCount;

return candidates;

}

class App extends Component {

constructor(props) {

super(props)

this.state = {

candidates: [

{

“name”: “Rama”,

“id”: 100,

“votingCount”: 0

},

{

“name”: “Nick”,

“id”: 101,

“votingCount”: 0

},

{

“name”: “Jose”,

“id”: 102,

“votingCount”: 0

},

{

“name”: “liyuechun”,

“id”: 103,

“votingCount”: 0

}

],

candidatesVoteCount: [“0″,”0″,”0″,”0”],

web3: null

}

}

componentWillMount() {

// Get network provider and web3 instance.

// See utils/getWeb3 for more info.

getWeb3

.then(results => {

this.setState({

web3: results.web3

})

// Instantiate contract once web3 provided.

this.instantiateContract()

})

.catch(() => {

console.log(‘Error finding web3.’)

})

}

instantiateContract() {

/*

* SMART CONTRACT EXAMPLE

*

* Normally these functions would be called in the context of a

* state management library, but for convenience I’ve placed them here.

*/

const contract = require(‘truffle-contract’)

const votingContract = contract(VotingContract)

votingContract.setProvider(this.state.web3.currentProvider)

// Declaring this for later so we can chain functions on SimpleStorage.

// Get accounts.

this.state.web3.eth.getAccounts((error, accounts) => {

votingContract.at(contractAddress).then((instance) => {

votingContractInstance = instance;

for (let i = 0; i < this.state.candidates.length; i++) {

let object = this.state.candidates[i];

console.log(accounts[0]);

console.log(votingContractInstance);

console.log(votingContractInstance.totalVotesFor(object.name));

votingContractInstance.totalVotesFor(object.name).then(result => {

console.log(i);

console.log(result.c[0]);

this.setState({

candidates: _modifyVotingCount(this.state.candidates,i,result.c[0])

});

});

}

})

})

}

render() {

return (

<div className=”App”>

<ul>

{

this.state.candidates.map((object) => {

console.log(object);

return (

<li key={object.id}>候选人:{object.name}          支持票数:{object.votingCount}</li>

)

})

}

</ul>

<input

style=

placeholder=”请输入候选人姓名…”

ref=”candidateInput”

/>

<button style= onClick={() => {

console.log(this.refs.candidateInput);

console.log(this.refs.candidateInput.value);

let candidateName = this.refs.candidateInput.value;

console.log(this.state.web3.eth.accounts[0]);

votingContractInstance.voteForCandidate(candidateName).then((result => {

console.log(result);

console.log(candidateName);

let number = 0;

for(let i = 0; i < this.state.candidates.length; i++) {

let object = this.state.candidates[i];

if (object.name === candidateName) {

number = i;

break;

}

}

votingContractInstance.totalVotesFor(candidateName).then(result => {

this.setState({

candidates: _modifyVotingCount(this.state.candidates,number,result.c[0])

});

});

}));

}}>Voting</button>

</div>

);

}

}

export default App

去中心化投票App



版权声明:本文为welling_22原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。