以太坊智能合约开发DApp应用示例
智能合约开发是以太坊的核心,学习过程主要是搭建以太坊开发环境和solidity语言的编程。本文不用任何以太坊Dapp框架,直接在ganache下开发智能合约。
让我们构建一个简单的"Hello World!" 应用程序, 这是一个投票应用程序。
该应用程序非常简单,它所做的只是初始化一组候选人,让任何人投票给候选人,并显示每个候选人收到的总票数。
我有意避免使用任何DAPP框架构建这个应用程序,因为框架抽象掉很多细节,你不了解系统的内部。此外,当你使用框架时,将对框架所做的繁重工作有更多的体会!
我们的目标是:
- 建立开发环境。
- 学习编写智能合约
1. 设置开发环境
我们使用一个模拟的内存区块链(ganache)代替真实的区块链在进行开发。在本教程的2章,我们将与真实的区块链交互。下面是安装ganache、web3js的步骤,然后在linux上启动一个测试链。在macOS上安装过程也是一样的。
你可以看到ganache-cli自动创建了10个测试账号,每个账号预分配了100(虚构的)ethers
如果需要更详细的开发环境安装教程,可以参考如下文章:
2.简单的投票合约
我们将使用solidity编程语言来编写我们的合约。如果您熟悉面向对象编程,学习编写solidity合约应该是轻而易举的事。我们将编写一个合约对象,含有一个构造函数初始化候选人数组。合约对象有2个方法:
- 返回候选人获得的总票数
- 增加候选人的投票数。
注意:构造函数只被调用一次,当您部署合约到区块链。不像在网络世界里的每一个部署你的代码覆盖旧的代码,部署后的代码在区块链上是不变的。例如,如果你更新你的合约并且再次部署,旧合约仍然会在区块链上, 它所存储的数据不受影响,新的部署将创建一个新实例的合约。
下面是投票合约的代码:
pragma solidity ^0.4.18;
// 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) public {
candidateList = candidateNames;
}
// This function returns the total votes a candidate has received so far
function totalVotesFor(bytes32 candidate) view public returns (uint8) {
require(validCandidate(candidate));
return votesReceived[candidate];
}
// This function increments the vote count for the specified candidate. This
// is equivalent to casting a vote
function voteForCandidate(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;
}
}
复制上面的代码,在hello_world_voting目录下创建一个Voting.sol文件。现在让我们来编译代码并将其部署到ganache的区块链上.
为了编译solidity代码,我们需要安装名字为solc的npm模块
~/hello_world_voting$ npm install solc
我们将在node控制台中使用这个库来编译我们的合约。在上一篇文章中我们提到,web3js是一个让我们可以通过rpc访问区块链的库。我们将使用该库来部署我们的应用程序并与之交互。
首先,在命令行中断运行node
命令进入node控制台,初始化solc和文本对象。下面的所有代码片段都需要在node控制台中键入
~/hello_world_voting$ node
> Web3 = require('web3')
> web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
为了确保web3对象已经初始化、区块链能够访问,让我们试一下查询区块链上的所有账户。您应该看到如下的结果:
> 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)
当你的代码编译成功并打印了合约对象的内容(在node控制台中输出的内容),有2个字段很重要,需要理解它们:
-
compiledCode.contracts[‘:Voting’].bytecode
: Voting.sol源代码编译后得到的字节码。这是将被部署到blockchain的代码。 -
compiledCode.contracts[‘:Voting’].interface
: 合约接口或模板(称为ABI)告诉用户合约含有哪些方法。您需要这些ABI的定义,因为将来你总是需要与合约交互的。更多ABI信息请参考这里。
如果希望能在线学习以太坊DApp的开发,分享一个教程:
http://xc.hubwiz.com/course/5a952991adb3847553d205d1
- Silverlight button 图片切换样式
- 【腾讯云CDB】教你玩转MyRocks/RocksDB—STATISTICS与后台线程篇
- Caliburn.Micro学习笔记(一)----引导类和命名匹配规则
- SpringMVC下Excel文件的上传下载
- Spring-AOP之aspectj注解方式
- wpf键盘记录器
- WPF之TreeList的实现方法(一)
- Silverlight 2 Beta 2的Isolated Storage
- hive安装部署
- java匿名内部类简介
- 精典算法之详解 河内之塔
- 精典算法之二分查找法
- 指针数组和数组指针
- 用sp_change_users_login消除Sql Server的孤立用户
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 快速解释如何使用pandas的inplace参数
- bam格式转bigWig
- 46. Vue路由传参的基本使用
- H3C在端口同时配置MAC地址认证和802.1x
- proxmox notes
- Unix每分钟监控进程的状态
- webpack实战——资源输入与输出
- R语言读取 xlsx 和xls 文件
- pytest文档42-fixture参数化params
- 搭建node服务(三):使用TypeScript
- Antd for Vue使用Form组件报错You cannot set a form field before rendering
- IDEA配置Resin
- BERT详解(附带ELMo、GPT介绍)
- Centos7创建LVM及扩容
- python3 使用session模拟post实现修改活码内容