【精品】以太坊DApp开发入门实战-区块链投票系统****

本视频课程面向初学者,介绍什么是区块链,什么是智能合约,内容涵盖以太坊开发相关的基本概念,Gregory(Dapp University 创始人,专注于讲解在以太坊区块链上构建DApp应用程序教程。Gregory大神的以太坊课程在youtube上超过五千位订阅者,所有课程超过10万人次观看,深受区块链技术开发用户的喜爱 )大神在课程中手把手地教大家如何构建一个基于以太坊的完整去中心化应用 —— 区块链投票系统。


课程介绍

本视频课程面向初学者,介绍什么是区块链,什么是智能合约,内容涵盖以太坊开发相关的基本概念,Gregory大神在课程中手把手地教大家如何构建一个基于以太坊的完整去中心化应用 —— 区块链投票系统。

通过本课程的学习,你将掌握:

1、以太坊区块链开发的基本知识

2、开发和部署以太坊合约所需的软件环境

3、使用高级语言(solidity)编写以太坊智能合约

4、使用NodeJS编译、部署合约并与之交互

5、使用Truffle框架开发分布式应用

6、使用控制台或网页与合约进行交互


课程内容


【精品】以太坊DApp开发入门实战-区块链投票系统****



什么是区块链?

我们用一个比喻来明白什么是区块链,它的工作原理是怎样?让我们先看一下网页应用程序。

【精品】以太坊DApp开发入门实战-区块链投票系统****

当我们和网页应用程序交互时,你使用网页浏览器去连接我们的应用中心服务器。所有的网页应用都基于中心服务器,所有的数据都在中心数据库。任何时候在应用程序上交易,你都需要和网页中心服务器沟通。

如果我们在网页应用程序上投票选举,投票选举时我们会有以下问题:

1. 数据库里的数据可能会被改变:或者重复投票:也可能投票数据全部被删除。

2. 网页服务器上的源代码在任何时候都可能会被改变。

我们不愿意在网页上创建应用程序。我们更愿意把它创建在大家能连接的区块链上,确保一人投票一次,且不可篡改。让我们看看在区块链上怎么去实现它。


区块链不是一个*服务器和一个数据库,而是一个网络和一个数据库。区块链是计算机的对等网络,称为节点,共享网络中的所有数据和代码。因此,如果您是连接到区块链的设备,则您是网络中的一个节点,并且您可以与网络中的所有其他计算机节点通话。您也拥有区块链上所有数据和代码的副本。区块链上没有*服务器,只有不同设备之间在同属性下的网络点对点交流。

【精品】以太坊DApp开发入门实战-区块链投票系统****

【精品】以太坊DApp开发入门实战-区块链投票系统****

区块链取代中心化数据库,所有的交易都在区块链上,所有包含的记录称之为区块。所有连接起来的区块叫区块链,区块链之间创建公共账本,公共账本代表区块链里面的所有数据。


公共账本的所有数据都通过加密技术哈希密码进行保护,并通过共识算法进行验证。网络上节点的参与确保网络上分散的所有数据副本都是一样的。这就是我们为什么要在区块链上构建投票应用程序,因为我们希望确保我们的投票都被计算在内,而且没有被篡改。

如果我们的投票应用程序应用在区块链上会怎么样?

对于新用户来讲,新用户需要一个带钱包地址和有ETH的账户。ETH也就是以太坊的加密货币。一旦连接到区块链网络,投票会在区块链上消耗一些ETH。

这些交易费用称之为“燃气”。当开始投票是,网络上帮助完成这笔交易的矿工将获得这笔交易费用的ETH。我投票的记录就永远被记录下来了。

记住一点:投票是一种交易,交易会消耗ETH,但读取数据是免费的。


什么是智能合约?

智能合约就是在以太坊虚拟机(EVM) 上执行我们代码的过程。

智能合约的编程语言是Solidity,有点像Javascript,但有点不一样。Solidity编程语言可以实现我们所有的商务交易逻辑。

如果说公共账本代表网页的数据库,那么智能合约就是实现所有商务逻辑交易的地方。

现在让我们快速的看看我们构建的DApp的结构。

【精品】以太坊DApp开发入门实战-区块链投票系统****

【精品】以太坊DApp开发入门实战-区块链投票系统****

传统的前端应用程序用的是HTML, CSS, 和 Javascript.语言。

代替传统的前端应用程序是后端服务器,客户安装连接本地以太坊区块链,去用Solidity语言编译去中心化选举。部署智能合约到本地区块链上进行投票选举。

让我们一起来看看区块链的工作原理,为什么我们要用区块链代替下当前的中心化应用程序。


我们要创建什么?

我们将构建一个客户端应用程序,与我们在区块链上的智能合约进行对话。这个客户端应用程序将有一个候选人列表,列出每个候选人的ID,姓名和投票数。 它会有一个表格,我们可以为我们想要的候选人投一票。 它还显示我们在“您的帐户”下连接到区块链的帐户。


安装依赖项

创建DApp之前, 首先需要安装依赖项.


节点包管理器 (NPM)

我们需要的第一个依赖是Node Package Manager或NPM,它随Node.js一起的。你可以看看你的节点是否已经安装了,你可以去你的终端并输入:

$ node -v


Truffle 框架

下一个依赖项是Truffle框架它可以使我们在以太坊区块链上创建分布式应用,提供一套用solidity编译的智能合约工具,也可以帮助我们测试智能合约部署区块链,它也为我们开发客户端应用程序提供了一个地方。在命令行中这样安装truffle和NPM:

$ npm install -g truffle


Ganache

下一个依赖项是Ganache,一个本地内存区块链。你也可以在Truffle Framework 网址下载安装Ganache。它将会提供以太坊外部账户,每个账户里都有100个假的ETH和它的钱包地址。


Metamask谷歌扩展插件

我们下一个要安装的依赖项是:Metamask谷歌扩展插件。Metamask帮助我们用个人账户连接本地区块链网络进行交互。开始之前,记得安装Metamask谷歌扩展插件哦。


语法高亮(Syntax Highlighting)

下一个依赖项是语法高亮,很多编辑器和IEDs没有语法高亮,我建议在用solidity编程是安装语法高亮。所以你要安装一个这样的安转包,教程里用我用的是Sublime Text,我已经在“以太坊”包下载了这个语法高亮。


  1. Smoke Test - 步骤一

好了,现在我们所以的依赖项都安装好了,让我们开始创建我们的DApp吧。

首先,下载Ganache并打开,确保你的本地区块链正常运作

Ganache给我们10个账户,每个账户100个假的ETH。

个帐户都有一个唯一的地址和一个私钥。每个帐户地址将作为我们选举中每个选民的唯一标识符。现在让我们创建一个Dapp的项目目录:

$ mkdir election

$ cd election


现在我们进入项目内部了,Truffle box使其快速运行。这个教程我们使用PET Shop box.从您的项目目录中, 像这样从命令栏安装 pet shop box:

$ truffle unbox petshop


看看 pet shop box给我们带来什么:【精品】以太坊DApp开发入门实战-区块链投票系统****

【精品】以太坊DApp开发入门实战-区块链投票系统****

· contracts 目录: this is where all smart contacts live. We already have a Migration contract that handles our migrations to the blockchain.

这就是智能合约所在的地方。在区块链上我们已经有Migration(迁移)合约处理我们的迁移。


· migrations 目录:是所有migration文件所在地方。当在区块链上更新区块链状态时就需要migration。就像网页开发框架里面需要migration来更新数据状态一样。


· node_modules 目录: 这是所有的节点依赖项的大本营。


· src 目录: 这是我们开发客户端应用程序的地方。


· test 目录: 这是我们为智能合约编写测试的地方。


· truffle.js 文件:这是 Truffle项目的主要配置文件.


让我们一起来在合约目录下创建新合约吧:

$ touch contracts/Election.sol


我们首先创建一个“烟雾测试”,以确保我们已经正确设置了项目,并成功地将合约部署到区块链。打开文件并从下面的代码开始:

pragma solidity 0.4.2;

contract Election {

// Read/write candidate

string public candidate;


// Constructor

function Election () public {

candidate = "Candidate 1";

}

}


现在我们已经为智能合约创建了基础,让我们看看是否可以将其部署到区块链。因此,我们需要在migrations目录中创建一个新文件。从您的项目根目录中,像这样从命令行中创建一个新文件:

$ touch migrations/2_deploy_contracts.js


请注意,我们使用数字为migrations目录中的所有文件编号,以便Truffle知道执行它们的顺序。让我们创建一个这样新的migration部署的合约:

var Election = artifacts.require("./Election.sol");

module.exports = function(deployer) {

deployer.deploy(Election);

};


首先,我们需要创建合约,并将其分配给名为“Election”的变量。 接下来,我们将其添加到已部署合同的清单中,以确保在我们运行migrations时将其部署。 现在让我们像这样从命令行中运行migrations

$ truffle migrate:


现在我们已经成功将我们的智能合约迁移到当地的以太坊区块链, 让我们打开控制台与智能合约交互。 你可以像这样从命令栏中打开truffle控制器:

$ truffle console


现在我们进入了控制台,让我们来看看我们部署的智能合约的一个实例,看看我们是否可以从合约中读取候选人的名字。从控制台运行此代码:

Election.deployed().then(function(instance) { app = instance })


这里的Election 是我们在migration文件中创建的变量的名称。我们在这个合约例子里检索了一个部署deployed() 函数,分配给一个app变量在promise的回调函数中。现在我们可以像这样读取候选变量的值:

app.candidate()

// => 'Candidate 1'


恭喜你!你刚刚编写了第一份智能合约,并将其部署到区块链中,并检索了一些数据。


步骤二:候选人名单。

我们需要一种方法来存储多个候选人,并存储关于每个候选人的多个属性。我们希望跟踪候选人的ID,姓名和投票数。 以下是我们将如何对候选人进行模拟:

contract Election {

// Model a Candidate

struct Candidate {

uint id;

string name;

uint voteCount;

}

// ...

}


我们使用Solidity Struct为候选人建模,Solidity允许我们创建自己的结构类型,就像我们在这里为候选人所做的那样。 我们指定这个结构有一个无符号整数类型的ID,字符串类型的名称和无符号整数类型的voteCount。 简单地声明这个结构体实际上不会给我们一个候选人。 我们需要实例化它并在将它写入存储之前将其分配给一个变量。


接下来我们需要的是一个储存候选人的地方。我们需要一个地方来存储我们刚刚创建的一种结构类型。我们可以使用Solidity映射进行此操作。 Solidity中的映射类似于关联数组或哈希,它将键 - 值对关联起来。 我们可以像这样创建这个映射:

contract Election {

// Model a Candidate

struct Candidate {

uint id;

string name;

uint voteCount;

}

// Read/write Candidates

mapping(uint => Candidate) public candidates;

// ...

}


在这种情况下,映射的关键是一个无符号整数,并且该值是我们刚刚定义的Candidate结构类型。 这基本上给了我们一个基于id的查找每个候选人。 由于该映射被分配给一个状态变量,因此只要我们为其分配新的键值对,我们就会将数据写入区块链。 接下来,我们将该映射的可见性设置为public,以获得getter函数,就像我们在烟雾测试中使用候选名称一样。


接下来,我们通过一个像这样的计数器缓存状态变量来跟踪选举中存在多少候选者:

contract Election {

// Model a Candidate

struct Candidate {

uint id;

string name;

uint voteCount;

}

// Read/write Candidates

mapping(uint => Candidate) public candidates;

// Store Candidates Count

uint public candidatesCount;

// ...

}


在Solidity中,无法确定映射的大小,也无法对它进行迭代。 这是因为尚未分配值的映射中的任何键都会返回默认值(在这种情况下为空候选)。 例如,如果我们在这次选举中只有2名候选人,并且我们试图查找候选人#99,那么映射将返回一个空的候选人结构。 这种行为使得不可能知道有多少候选者存在,因此我们必须使用计数器缓存

接下来,我们创建一个函数,将候选添加到我们创建的映射中:

contract Election {

// ...

function addCandidate (string _name) private {

candidatesCount ++;

candidates[candidatesCount] = Candidate(candidatesCount, _name, 0);

}

}


我们已经声明了函数addCandidate,它接受一个表示候选人姓名的字符串类型参数。 在函数内部,我们递增候选计数器缓存以表示添加了新的候选项。 然后我们用一个新的Candidate结构更新映射,使用当前的候选计数作为关键。 这个Candidate结构体使用来自当前候选计数的候选id,函数参数中的名称以及初始投票计数初始化为0.请注意,此函数的可见性是私有的,因为我们只想在合同中调用它。

现在,我们可以通过在构造函数内两次调用“addCandidate”函数来添加两个候选人,如下所示:

contract Election {

// ...

function Election () public {

addCandidate("Candidate 1");

addCandidate("Candidate 2");

}

// ...

}


当我们将合同部署到区块链时,这个迁移将会执行,并且会有两名候选人参加我们的选举。此时,您的完整合同代码应如下所示:

pragma solidity ^0.4.2;

contract Election {

// Model a Candidate

struct Candidate {

uint id;

string name;

uint voteCount;

}

// Read/write candidates

mapping(uint => Candidate) public candidates;

// Store Candidates Count

uint public candidatesCount;

function Election () public {

addCandidate("Candidate 1");

addCandidate("Candidate 2");

}

function addCandidate (string _name) private {

candidatesCount ++;

candidates[candidatesCount] = Candidate(candidatesCount, _name, 0);

}

}


现在让我们像这样迁移我们的合同:

$ truffle migrate --reset


现在尝试与控制台内的候选人进行交互。

现在让我们编写一些测试来确保我们的智能合约正确初始化。首先,让我解释为什么在开发智能合同时测试如此重要。


Testing

现在我们来写一些测试。确保你有Ganache第一次运行。 然后,从项目根目录的命令行中创建一个新的测试文件,如下所示::

$ touch test/election.js

我们将用the Mocha testing framwork和the Chai assertion library在这个文件中使用Javascript编写我们所有的测试。这些与Truffle框架捆绑在一起。 我们将使用Javascript编写所有这些测试,以模拟客户端与我们的智能合约的交互,就像我们在控制台中所做的一样。 这里是测试的所有代码:

var Election = artifacts.require("./Election.sol");

contract("Election", function(accounts) {

var electionInstance;

it("initializes with two candidates", function() {

return Election.deployed().then(function(instance) {

return instance.candidatesCount();

}).then(function(count) {

assert.equal(count, 2);

});

});

it("it initializes the candidates with the correct values", function() {

return Election.deployed().then(function(instance) {

electionInstance = instance;

return electionInstance.candidates(1);

}).then(function(candidate) {

assert.equal(candidate[0], 1, "contains the correct id");

assert.equal(candidate[1], "Candidate 1", "contains the correct name");

assert.equal(candidate[2], 0, "contains the correct votes count");

return electionInstance.candidates(2);

}).then(function(candidate) {

assert.equal(candidate[0], 2, "contains the correct id");

assert.equal(candidate[1], "Candidate 2", "contains the correct name");

assert.equal(candidate[2], 0, "contains the correct votes count");

});

});

});


第一个测试通过检查候选人数量是否等于2来检查合同是否被初始化了正确的候选人数。

下一个考试考查每个候选人在选举中的价值,确保每个候选人都有正确的ID,姓名和投票数。

现在让我们像这样从命令行运行测试:

$ truffle test


看,编译通过。