什么是智能合约(Smart Contract):
最简单的理解,我们所说的智能合约就是区块链上自动运行的一段程序。而基于Ethereum的智能合约就是我们接下来所要学的以太坊智能合约。这里,在我们的Smart Contract中,具体的代码经过solidity编写后,发布到区块链上。而以太坊的智能合约也可以理解为一个特殊的交易(包括可执行的代码),被发送出去后会被矿工打包记录在某一区块中。
在这一系列笔记中,我们将以实例为基础,讲解智能合约开发的流程。首先我们将一起建立一个基础的单员工薪酬系统,并在后续一步步进行完善。
源码地址:https://github.com/Chlover/Team-J/blob/master/Lesson-1/orgin/payroll.sol
版本声明:
//声明程序版本
pragma solidity ^0.4.14
因为Solidity大多是开源的程序,所以要在程序的最开始声明程序版本,方便社区合作。
^
表示向上兼容,^0.4.14
表示solidity
的版本在0.4.14~0.5.0(不包含0.5.0)
的版本都可以对上面的合约代码进行编译,0.4.18
,0.4.20
等等可以用来修复前面solidity存在的一些bug
。
声明Contract:
// 声明一个Contract
contract Payroll {
}
Solidity是一门面向对象编程的语言,语法较为贴近JavaScript。其中合同的声明就类似于声明一个Class。
状态变量声明:
// 声明一个Contract
contract Payroll {
address Owner;
uint salary;
address employee;
uint lastPayday;
}
变量初始值均为0状态,address变量初始化即为0x0。
另外注意在solidity源代码中,地址就是一串十六进制数,不需要加双引号,但是在Remix的对话界面中输入address时,一定要加双引号,不然会报错。这是因remix传入是是json格式,对于不加双引号的值会作为为int处理,加了引号才算bignumber。这里的int指的是js的int,地址明显超过范围,类似的输入一个超大int256数也要加引号。
构造函数:
function Payroll() {
owner = msg.sender;
}
函数名和合约名相同时,此函数是合约的构造函数,当合约对象创建时,会先调用构造函数对相关数据进行初始化处理。此处相当于在合约创建时就直接将合约的创建者设为owner,后续将保证只有owner可以更新雇员信息。
具体功能的函数实现:
function addFund() payable returns (uint) {
return this.balance;
}
用addfund函数向合约里加钱。值得注意的是payable
关键字,当函数有payable
关键字的时候,就表明即可接受ether,并会把ether存在当前合约。http://solidity.readthedocs.io/en/develop/frequently-asked-questions.html?highlight=payable
returns
后面加函数返回值的类型。
接下来这一部分我们要实现的是更新雇员信息的功能:
function updateEmployee(address e, uint s) {
require(msg.sender == owner);
if (employee != 0x0) {
uint payment = salary * (now - lastPayday) / payDuration;
employee.transfer(payment);
}
employee = e;
salary = s * 1 ether;
lastPayday = now;
}
首先可以发现新出现require
语句。后面我们还将见到assert
,这里我们放在一起讲解:
require
与assert
都是用于处理error的,二者可以用于判断某一条件,若条件不被满足则throw exception
。其中,require
主要用于检查输入值,而assert
则主要用于检查internal error
。
另外,触发assert
会消耗所有的gas,而触发require
则会把剩余的gas返还。
assert(false)
compiles to 0xfe
, which is an invalid opcode, using up all remaining gas, and reverting all changes.
require(false)
compiles to 0xfd
which is the REVERT
opcode, meaning it will refund the remaining gas.中间我们略过一下简单的函数不讲,直接进行最后getPaid函数的说明:
function getPaid() {
require(msg.sender == employee);
uint nextPayday = lastPayday + payDuration;
assert(nextPayday < now);
lastPayDay = nextPayday;
employee.transfer(salary);
}
这里我们就应用了上面提到的require
,assert
,这里的require
要求调用getPaid也就是领取工资的人一定要是雇员。
对于employee.transfer(salary)
和前面我就已经见到的msg
,我们将在下一篇文章中一起总结。
接下来,我们将在https://remix.ethereum.org测试我们写的智能合约
首先,我们现在的合约的environment是JavaScript VM,也就是没有真正部署上链的,在后面我们会讲到部署方法,但是最开始的阶段,我们的合约就在JavaScript VM上进行测试。选择以后,remix会自动帮我们生成一些地址,自己也可已经添加,供我们测试使用。
在Settings中我们可以选择solidity版本,由于是向上兼容的,我们只要选择高于0.4.14的版本即可,系统会自动为我们compile,没有报错的话就可以点击Create按钮了。
我们会注意到右上角的Value有一个单位wei
这是solidity里的最小单位,我们比较常用的是ether
,1 ether = 10^18 wei
。这时候我们在Value填入我们想add的数值,并调用addFund函数就可以将该数量金额的eth转入合约。
切记,在测试updateEmployee和getPaid函数时,要把右上角的Account换成相应的owner或employee地址,这样才能满足require(msg.sender == 相应地址)
。
到目前为止,我们就跟着流程把一个简单的单员工薪酬系统的合约运行了起来,对Solidity也有了大体的了解,在下一篇文章中,我们将对在Solidity的一些基础知识、语法进行补充。