在上一篇文章中,通过和传统的 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. python全栈开发day40-浮动的四大特性,浮动带来的问题和解决问题,文本属性、字体属性和颜色介绍

    一.昨日内容总结 1.盒模型及其属性 2.文本级标签.行内块.块级标签 3.继承性.层叠性.权重 4.浮动四大特性 # 浮动元素脱离标准文档流 # 贴靠 # 字围效果 # 自动收缩或紧缩 二.今日内容 ...

  2. KNN分类算法及python代码实现

    KNN分类算法(先验数据中就有类别之分,未知的数据会被归类为之前类别中的某一类!) 1.KNN介绍 K最近邻(k-Nearest Neighbor,KNN)分类算法是最简单的机器学习算法. 机器学习, ...

  3. 64位JDK+tomcat6+myeclipse 10安装与配置

    一.安装JDK与配置环境与检验配置成功: 1.进入java.com网站,然后按照以下步骤进行  =>=>=>=>=> =>=>等会出现java茶杯双击,一次一 ...

  4. 093实战 Nginx日志切割,以及脚本上传nginx的切割日志

    一:日志切割步骤 命令都在root下进行 1.创建目录 mkdir -p /etc/opt/modules/bin ## 创建文件夹 2.上传cut 3.观察目录 4.修改的cut文件 5.检测 需要 ...

  5. 全排列问题(递归&非递归&STL函数)

    问题描述: 打印输出1-9的所有全排序列,或者打印输出a-d的全排列. 思路分析: 将每个元素放到余下n-1个元素组成的队列最前方,然后对剩余元素进行全排列,依次递归下去. 比如:1 2 3 为例首先 ...

  6. CentOS root用户修改密码

    1.root用户修改密码: #passwd -------------------------------- 参考资料: 1.Centos修改root密码:http://blog.163.com/wz ...

  7. metasploit常用服务扫描和利用模块

    metasploit常用服务扫描和利用模块 SMB扫描 smb枚举auxiliary/scanner/smb/smb_enumusers 扫描命名管道auxiliary/scanner/smb/pip ...

  8. Python Django 学习 (一) 【Django 框架初探】

    1. 简介: Python下有许多款不同的 Web 框架.Django是重量级选手中最有代表性的一位.2008年9月发布第一个版本,目前的Django版本应该是2.1. 2. 本文的环境 OS : W ...

  9. 微信小程序:一起玩连线,一个算法来搞定

    微信小程序:一起玩连线 游戏玩法 将相同颜色的结点连接在一起,连线之间不能交叉. 算法思想 转换为多个源点到达对应终点的路径问题,且路径之间不相交.按照dfs方式寻找两个结点路径,一条路径探索完之后, ...

  10. MongoDB——权限管理

    MongoDB--权限管理 MongoDB默认是没有权限验证的,但生产环境中,没有权限控制是很不安全的. 我们先不详谈太多概念,直接动手创建两个典型的账号: 超级管理员,类似sql server的sa ...