在上一篇文章中,通过和传统的 web程序相比较解释了以太坊平台的结构。作为一个开发者,学习新技术的最好的方式就是构建一个玩具程序。
在这篇文章中我们将会构建一个简单的“hello word”程序,这个程序是一个投票程序。
 
这个程序非常简单,包括:初始化一个参加者集合,让任何人为候选人投票,显示每一个候选人获得的投票数。我们的目的不仅仅是编写一个应用,我们的目的是学习应用编译,部署,交互的过程。
 
我故意的避免使用任何Dapp框架来构建这个应用,因为框架抽象了很多细节,这样你就不能够很好的理解系统的细节。而且,当你使用框架的时候,会评估这个框架给你解决的繁重的工作。
 
总的来说这章是对上一篇文章的延续,如果你是刚接触Ethereum,我建议你最好读一读上一篇文章。
 
我们本次练习的目的:
1,搭建开发环境
2,学习在开发环境下编写,编译,部署合约。
3,在区块链上通过node.js控制台利用合约进行交互。
4,通过一个简单的web页面利用合约来交互,通过这个页面显示投票数,以及每个候选人的获得的投票数。
 
整个应用部署与构建在一个新的ubuntu 16.04 机器上,我在macos上也很好的启动运行这个程序。
 
下面是形象化构建我们现在的应用程序的方式。
 

1,搭建开发环境:

我们这里不是基于活跃的区块链的开发app,而是使用一个叫做testrpc的内存区块链。在第二部分的教程中,我们将会在真正的区块链上进行交互,
下面来安装testrpc,web3js以及在linux环境中启动一个测试区块链。这个结构同样可以完全运行在在macos系统下。
对于windows用户来说你可以使用下面的方式:https://medium.com/@PrateeshNanada/steps-to-install-testrpc-in-windows-10-96989a6cd594
 
注意:这个教程当前工作的web3js的版本是0.20.1,通常我们会运行npm install ethereumjs-testrpc web3@0.20.1
而不是运行npm install ethereumjs-testrpc web3 ,在web3js的1.0文档版发布之后我会更新这个教程。
 

注意testrpc在自动运行的时候会自动创建10个测试帐号。这些帐号都预装了100个假的以太网节点。
 

2,简单的投票合约:

我们使用solidity语言来编写我们的合约,如果你对面向对象语言熟悉的话,学solidity编写智能合约将会很容易。
我们编写的智能合约叫做Voting(在你熟悉的面相对象语言中想象合约就是一个类),Voting有一个初始化候选人的数组结构。
我们将会写两个方法,一个是返回候选人获得的总选票,另一个是给候选人加票的方法。
 
注意:当我们把合约部署到区块链上的时候,构造函数只能被调用一次,和web世界不同,web世界中你的代码部署的时候你可以使用新代码来覆盖以前的老代码,
但是在区块链上部署的代码是不可更改的。如果你更新合约重新部署代码,旧的合约以及数据依然在区块链上。新部署的将会创建一个新的合约实例。
 
下面是一个投票合约的代码,每一行都有注释:
pragma solidity ^0.4.;
// 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] += ;
} function validCandidate(bytes32 candidate) returns (bool) {
for(uint i = ; i < candidateList.length; i++) {
if (candidateList[i] == candidate) {
return true;
}
}
return false;
}
}
拷贝下面代码到hello_world_voting目录的文件Voting.sol文件中,现在让我们来编译代码,并把它部署到testrps上面。
 
编译solidity代码,首先要通过npm安装npm module:solc

mahesh@projectblockchain:~/hello_world_voting$ npm install solc
我们将会使用这个带有node.js控制台的包,来编译我们的智能合约,从上一章我们知道,web3js是一个让你通过RPC来和区块链交互的包。
我们将会使用web3js这个包来进行部署与交互。
 
首先,在控制台运行node命令来调用node控制台,并初始化solc和web3js对象。下面所有的代码片段,都需要在node的交互式环境下。

mahesh@projectblockchain:~/hello_world_voting$ node

> Web3 = require('web3')
> web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
为了确保web3js对象被初始化了,并且可以和区块链通信,让我们来查询所有在区块链上的账户。你会看到类似下面的输出结果:

> 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)
当你成功编译了代码,打印了合约对象(仅仅是在控制台中查看到的上面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
 
现在我们来部署智能合约。首先你要创建一个合约对象(下面的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[], gas: })
> deployedContract.address
> contractInstance = VotingContract.at(deployedContract.address)
上面的VotingContract.new用来在区块链上部署智能合约。第一个参数是候选人数组,这些候选人在选举竞争中都是相对简单的。让我们来看一下第二个参数的hash中有什么内容:
    1,data:这是一个我们在区块链上部署的编译后的二进制代码。
    2,from:区块链必须记录谁部署了这个智能合约。在这个例子中我们选择第一个账户来作为这个智能合约的拥有者(将会部署这个合约到区块链上)。
这第一个账户我们是通过调用web3.eth.accounts来获取的。记得上面代码web3.eth.accounts返回一个数组,数组里面包含10个由testrpc创建的测试账户,
这10个账户是当我们在启动测试区块链的时候创建的。在真实活跃的区块链中,在没创建之前,你不能使用任何账户。你必须在交易(通信/交流)前拥有这个账户,并解锁他。
当你在创建账户的时候被要求填写密码,这个密码用来证明你和账户的关系。为了方便testrpc默认解锁了10个账户。
    3,gas:和区块链交互花费的钱,这些钱是给矿工的,矿工的所有工作是在区块链上引入你的代码。你必须指定你将会支付多少钱给把你的代码包含到区块链上的人。
这些钱就是通过设置gas的值来指定的。你的上面代码from中的账户的以太坊余额可以用来购买gas。gas的价格由网络来设定。
 
现在我们已经部署了智能合约并有了一个合约实例(上面的contractInstance变量)。我们可以使用这个合约来进行交互。
区块链上有成千上万的合约部署在上面。但是,在区块链上怎么辨别自己的合约呢?答案就是deployedContract.address。
当你利用你的合约进行交互的时候,你需要这个部署地址,以及我们上面提到的abi的描述。
 

3,在nodes控制台和合约进行交互

> contractInstance.totalVotesFor.call('Rama')
{ [String: ''] s: , e: , c: [ ] }
> contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[]})
'0xdedc7ae544c3dde74ab5a0b07422c5a51b5240603d31074f5b75c0ebc786bf53'
> contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[]})
'0x02c054d238038d68b65d55770fabfca592a5cf6590229ab91bbe7cd72da46de9'
> contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[]})
'0x3da069a09577514f2baaa11bc3015a16edf26aad28dffbcd126bde2e71f2b76f'
> contractInstance.totalVotesFor.call('Rama').toLocaleString()
''
在你的node交互控制台上试试上面的命令,你将会看到投票的数量增加。每次当你为一个候选人投票,你就会获得一个交易的ID:
例如:上面的:‘0xdedc7ae544c3dde74ab5a0b07422c5a51b5240603d31074f5b75c0ebc786bf53’这个交易ID是交易发生的证据。
将来你可以在任何时间来返回去查看他(数据可追踪)。这个交易是不可更改的。这种不可更改的特性是以太坊这种区块链的很大优势之一。
在接下来的教程中,我们将会利用她的不可更改性来构建应用。
 

4,web页面链接区块链和投票

现在所有的工作都完成了,现在我们所要做的就是构建一个包含候选人的简单的html文件。
并在一个js文件中调用投票命令(这个投票命令我们已经在前面的node控制台中测试过了)。下面你会看到html代码和js文件。
在hello_world_voting文件件下复制他们,并在你的浏览器中打开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>A Simple Hello World 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);
// In your nodejs console, execute contractInstance.address to get the address at which the contract is deployed and change the line below to use your deployed address
contractInstance = VotingContract.at('0x2a9c1d265d06d47e8f7b00ffa987c9185aecf672');
candidates = {"Rama": "candidate-1", "Nick": "candidate-2", "Jose": "candidate-3"} function voteForCandidate() {
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);
}
});
不知道你是否记得前面我们说过和任何合约进行交互必须需要abi和地址。在上面的index.js文件中你将会看到他们是怎么使用合约来进行交互的
 
下面是你在浏览器中打开index.html文件。
 

如果你可以进入上面文本框的候选人名字,并投票并且会看到投票增加。你已经成功构建了你的第一个应用。恭喜。
总结:你搭建了你的环境,编写了你的简单合约,编译以及部署合约到区块链上。
能够通过nodejs控制台进行交互,同时也能通过web页面进行同样交互。
 
在第二章,我们将会部署这个合约到一个公共的测试网络,这样整个世界都会看到他。并给候选人进行投票。
我们都已经很熟悉了并使用truffle框架来开发(不要使用node的控制台来管理整个过程)。
希望这个教程能够帮助你在以太坊平台上开发去中心化的应用的时候提供指导意义。
 
翻译自:https://medium.com/@mvmurthy/full-stack-hello-world-voting-ethereum-dapp-tutorial-part-1-40d2d0d807c2

Ethereum Dapp Tutorial — Part 1的更多相关文章

  1. Truffle 4.0、Geth 1.7.2、TestRPC在私有链上搭建智能合约

    目录 目录 1.什么是 Truffle? 2.适合 Truffle 开发的客户端 3.Truffle的源代码地址 4.如何安装? 4.1.安装 Go-Ethereum 1.7.2 4.2.安装 Tru ...

  2. ConsenSys/eth-lightwallet(browserless)

    https://github.com/ConsenSys/eth-lightwallet LightWallet A minimal ethereum javascript wallet.一个小型的钱 ...

  3. Solidity notes

    1. 查询transaction历史记录 https://forum.ethereum.org/discussion/2116/in-what-ways-can-storage-history-be- ...

  4. 以太坊(ethereum)开发DApp应用的入门区块链技术教程

    概述 对初学者,首先要了解以太坊开发相关的基本概念.   学习以太坊开发的一般前序知识要求,最好对以下技术已经有一些基本了解: 一种面向对象的开发语言,例如:Python,Ruby,Java... 前 ...

  5. 《区块链DAPP开发入门、代码实现、场景应用》笔记3——Ethereum Wallet的安装

    以太坊官方网站可以下载最新版本的Ethereum Wallet,用户无需选择,浏览器会根据访问者操作系统版本自动展现合适的版本,点击DOWNLOAD按钮下载即可安装,如图2.9所示,其下载网址: ht ...

  6. 从零构建以太坊(Ethereum)智能合约到项目实战——第23章 从零构建和部署去中心化投票App,decentralization Voting Dapp

    P90 .1-从零构建和部署去中心化投票App-01 P91 .2-从零构建和部署去中心化投票App-02 P92 .3-从零构建和部署去中心化投票App-03 参考博文:http://liyuech ...

  7. 《区块链DAPP开发入门、代码实现、场景应用》笔记4——Ethereum Wallet中部署合约

    账号创建完成之后,账号余额是0,但是部署合约是需要消耗GAS的,因此需要获取一定的以太币才能够继续本次实现.在测试网中获取以太币可以通过挖矿的方式,在开发菜单中可以选择打开挖矿模式,但是这需要将Syn ...

  8. 区块链入门到实战(21)之以太坊(Ethereum) – 分布式应用(DApp)

    作用:用户交互 分布式应用(DApp)是运行在区块链之上的应用程序,支持区块链网络中用户之间的交互. DApp(decentralized application)的后端代码运行在区块链网络上,这个可 ...

  9. 如何从零开始学习区块链技术——推荐从以太坊开发DApp开始

    很多人迷惑于区块链和以太坊,不知如何学习,本文简单说了一下学习的一些方法和资源. 一. 以太坊和区块链的关系 从区块链历史上来说,先诞生了比特币,当时并没有区块链这个技术和名词,然后业界从比特币中提取 ...

随机推荐

  1. Hadoop Yarn环境配置

    抄一个可行的Hadoop Yarn环境配置.用的官方的2.2.0版本. http://www.jdon.com/bigdata/yarn.html Hadoop 2.2新特性 将Mapreduce框架 ...

  2. 【Java】 剑指offer(13) 剪绳子

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 给你一根长度为n绳子,请把绳子剪成m段(m.n都是整数,n> ...

  3. 用VScode代码调试Python

    Python扩展支持许多类型的Python应用程序的调试,包括以下一般功能: 观看窗口 评估表达式 当地人 参数 扩大孩子 断点 条件断点 暂停(进入)正在运行的程序 自定义启动目录 要熟悉这些常规功 ...

  4. python中使用XPath笔记

    XPath在Python的爬虫学习中,起着举足轻重的地位,对比正则表达式 re两者可以完成同样的工作,实现的功能也差不多,但XPath明显比re具有优势,在网页分析上使re退居二线. XPath介绍: ...

  5. [CodeVS4438]YJQ Runs Upstairs

    [CodeVS4438]YJQ Runs Upstairs 题目大意: 一个\(n(n\le50)\)个点\(m(m\le300)\)条边的DAG,保证从\(1\)到\(n\)的所有路径经过边数均小于 ...

  6. 常用类及 LeetCode 每日一题

    1 日期时间类 在 Java 语言中,是通过时间戳来表示时间的.所谓的时间戳,在 Java 中就是指当前时间距离历元(1970-01-01 00:00:00)的时间间隔,单位是毫秒,所以 Java 中 ...

  7. Jenkins和maven自动化构建java程序

    转自:http://www.cnblogs.com/gao241/archive/2013/04/08/3008380.html,版权归原作者所有. Jenkins是一个非常出色的持续集成服务器,本文 ...

  8. ZwQuerySystemInformation枚举内核模块及简单应用

    简单说,即调用第11号功能,枚举一下内核中已加载的模块.部分代码如下://功能号为11,先获取所需的缓冲区大小ZwQuerySystemInformation(SystemModuleInformat ...

  9. 关于iphone6/5/4s 在iOS8.0系统下 仅仅读@3x图片

    做iphone6和plus适配时候发现一个问题,先来看图(一下讨论所有在真机下完毕,非模拟器) 128*750图片是  nav@2x.png watermark/2/text/aHR0cDovL2Js ...

  10. uva 1629切蛋糕(dp)

    有一个n行m列的网格蛋糕,上面有一些樱桃.求使得每块蛋糕上都有一个樱桃的分割最小长度 思路:dp. #include<cstdio> #include<cstring> #in ...