以太坊应用实战3-基于NodeJS开发编译部署DAPP

96
安福院长
2018.03.27 07:53* 字数 1279

在初步了解了以太坊基本概念之后,我们可以着手开始进行开发,此次我们将通过web3.js封装的以太坊接口来与区块链进行交互。应用整体结构如图所示:


3.1 以太坊应用结构

获得一个全节点相当耗时,本次实战将使用ganache软件模拟区块链节点,以便快速开发并测试应用。

为了便于开发和测试,ganache默认会自动创建10个账户,每个账户有100个以太币。


3.2 模拟区块链节点

上图所示,模拟节点的监听地址和端口为127.0.0.1:7545, 在使用web3.js时,我们将传入此地址告诉web3.js应当连接哪一个节点,接下来我们将使用第一个账号(0x627306090abaB3A6e1400e9345bC60c78a8BEf57)创建交易、发送/接受以太币。

投票合约设计

我们使用Solidity语言来编写合约,合约的属性用来保存合约的状态,合约中的方法为调用提供接口


3.3 Voting Class

投票合约包含以下内容:

* 构造函数Voting,用来初始化候选人名单

* 投票方法vote, 为指定的候选人投票数加1

*查询方法totalVotesFor(),查询指定候选人的得票数

有几点是区别以往

1. 合约的状态是持久化到区块链上,对合约状态的修改需要消耗费用

2. 只有部署合约到区块链上时,才会调用构造函数

3. 合约不可更改,如果想更新合约需要再次部署,但旧的合约仍会在区块链上,合约的状态也依然存在。

投票合约开发

合约代码保存在 voting.sol文件中

编译器要求

pragma solidity ^0.4.18

合约声明

contract Voting{}

contract 关键字用来声明一个合约

字典类型:mapping

mapping(bytes32 => uint8) public votesReceived;

votesReceived是一个键值对,其Key为候选者名字,类型为byte32字符串,值为每个候选者的得票数,类型为uint8

投票方法vote为指定的候选人投票。

function vote(byte32 candidate) public {

    require(validCandidate(candidate));

    votesReceived[candidate] += 1;

}

require 类似断言,只有条件为真时,合约才会继续执行。

validCandidate()方法判断指定的候选人是否是有效的候选人

function validCandidate(byte32 candidate) view public returns (bool) {

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

        if(candidateList[i] == candidate) {

            return true;

        }

    }

}

完整代码如下:

pragma solidity ^0.4.18;

contract Voting {

      mapping (bytes32 => uint8) public votesReceived;

      bytes32[] public candidateList;

      function Voting(bytes32[] candidateNames) public {

            candidateList = candidateNames;  }

      function totalVotesFor(bytes32 candidate) view public returns (uint8) {

        require(validCandidate(candidate));

            return votesReceived[candidate];

      }

      function vote(bytes32 candidate) public {

            require(validCandidate(candidate));

            votesReceived[candidate]  += 1;

      }

      function validCandidate(bytes32 candidate) view public returns (bool) {

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

                  if (candidateList[i] == candidate) {

                        return true;

                  }

        }

            return false;

      }

}

投票合约编译

启动node控制台,初始化web3对象,并向模拟的区块链节点(http://127.0.0.1:7545)查询所有账户

> Web3 = require('web3')

> web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

> web3.eth.accounts

[ '0x627306090abab3a6e1400e9345bc60c78a8bef57',  '0xf17f52151ebef6c7334fad080c5704d77216b732',  '0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef',  '0x821aea9a577a9b44299b9c15c88cf3087f3b5544',  '0x0d1d4e623d10f9fba5db95830f7d3839406c6af2',  '0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e',  '0x2191ef87e392377ec08e7c08eb105ef5448eced5',  '0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5',  '0x6330a553fc93768f612722bb8c2ec78ac90b3bbc',  '0x5aeda56215b167893e80b4fe645ba6d5bab767de' ]

编译合约,首先载入voting.sol的内容,之后使用solc编译器对合约代码进行编译

code = fs.readFileSync('voting.sol')

solc = require('solc')

compiledCode = solc.compile(code)


其编译后的代码有两个重要字段

bytecode: 投票合约编译后的字节码,将要被部署到区块链上

interface: 投票合约接口

投票合约部署

在node控制台下执行以下命令

abi = JSON.parse(compiledCode.contracts[':Voting'].interface)

contract = web3.eth.contract(abi)

byteCode = compiledCode.contracts[':Voting'].bytecode

deployedContract = contract.new(['zhangsan','lisi','wama'],{ data: byteCode,from:web3.eth.accounts[0], gas:4700000})

> deployedContract.address

'0x8cdaf0cd259887258bc13a92c0a6da92698644c0'

contractInstance = contract.at(deployedContract.address)

contract对象的new()方法将投票合约部署到区块链上,至此我们已经成功部署了投票合约,并且获得一个实例contractInstance,现在可以通过该实例与合约进行交互

合约交互

通过totalVotesFor方法查看候选人的票数

contractInstance.totalVotesFor.call('zhangsan')

{ [String: '0'] s: 1, e: 0, c: [ 0 ] }

vote()投票给候选人,下面的代码给zhangsan 投了二次票

> contractInstance.vote('zhangsan',{from:web3.eth.accounts[0]})

'0x7fd48761ba8f1fb83239ad6c5da1eff16cbec5259f6c60d28c5464523ac17f6f'

>contractInstance.vote('zhangsan',{from:web3.eth.accounts[0]})

'0xcd479e1b7d492dc608d584178adce1181e0d251974f70a1cac3f36c00a9eb27b'

再次通过totalVotesFor查看zhangsan的得票数

> contractInstance.totalVotesFor.call('zhangsan').toString()

'2'

每进行一次投票就产生一次交易,voteForCandidate()将返回交易的凭证,任何时候都可以通过交易凭证查看交易


3.4 transactions

总结

当前为止我们已经接触到的相关知识点

1. nodejs 和 ganache 作为开发环境开发编译部署合约

2. 开发了简单的投票合约,并编译部署到了区块链节点

3. 使用nodejs控制台与合约进行交互

4. 所有数据保存到了区块链,不可修改

5. 可独立验证每个候选人获得了多少投票

后续将使用Truffle框架构建此投票应用。

区块链
Web note ad 1