第一章 modifier函数修饰符
函数修饰符看起来跟函数没什么不同,不过关键字 modifier 告诉编译器,这是个modifier(修饰符),而不是个function(函数)。它不能像函数那样被直接调用,只能被添加到函数定义的末尾,用以改变函数的行为。函数修饰符也可以带参数。就像函数那样使用,例如:
// 存储蚂蚁级别的映射
mapping (uint => uint) public level;
// 限定蚂蚁等级的修饰符
modifier levelThan(uint _level, uint _antId) {
require(level[_antId] >= _level);
_;
}
// 必须年满5级才允许发奖励
// 我们可以用如下参数调用`levelThan` 修饰符:
function prize(uint _antId) levelThan(5, _antId) {
// 其余的程序逻辑
}
注意:prize 函数上的 levelThan 修饰符。 当你调用 prize 时,首先执行 levelThan 中的代码, 执行到 levelThan 中的 _ ; 语句时,程序再返回并执行 prize 中的代码。
实战
- 创建一个名为 moveCheck 的modifier,它接收2个参数, _originHouseId (uint类型) 以及 _targetHouseId (uint类型)。
- 运用函数逻辑确保蚂蚁搬家的两个房子 是不同的房子,_originHouseId 和 _targetHouseId 不能相同,如果一样就抛出一个错误,输出错误信息为 “不能在同一个房子来回搬东西”。
- 还需要确保蚂蚁搬家的原房子 houses[_originHouseId].existGoods 大于0,如果不大于0就抛出一个错误,输出错误信息为 “没东西可以搬啦,换一个房子吧”。
- 最后要确保蚂蚁搬家的目标房子 houses[_targetHouseId].maxGoods 大于 houses[_targetHouseId].existGoods,如果不超过就抛出一个错误,输出错误信息为 “这个房子放满啦,换一个房子吧”。
- 记住,修饰符的最后一行为 _;,表示修饰符调用结束后返回,并执行调用函数余下的部分。
- 修改 moveGoods 函数,让它带有一个 moveCheck 修饰符,调用的时候传 _originHouseId 和 _targetHouseId 参数。
pragma solidity ^0.4.20;
contract AntFamily {
event NewAnt(uint indexed antId, string name, uint dna);
event NewHouse(uint indexed houseId, string name, uint existGoods, uint maxGoods);
uint dnaDigits = 12;
uint dnaModulus = 10 ** dnaDigits;
struct Ant {
string name;
uint dna;
uint level;
uint moveCount;
}
struct House {
string name;
uint existGoods;
uint maxGoods;
}
Ant[] public ants;
House[] public houses;
mapping (uint => identity) public antToOwner;
mapping (identity => uint) ownerAntCount;
mapping (uint => identity) houseToOwner;
modifier moveCheck(uint _originHouseId, uint _targetHouseId) {
require(_originHouseId != _targetHouseId, "不能在同一个房子来回搬东西");
require(houses[_originHouseId].existGoods > 0, "没东西可以搬啦,换一个房子吧");
require(houses[_targetHouseId].maxGoods > houses[_targetHouseId].existGoods, "这个房子放满啦,换一个房子吧");
_;
}
function createAnt(string _name, uint _dna) {
uint id = ants.push(Ant(_name, _dna, 1, 0)) - 1;
antToOwner[id] = msg.sender;
ownerAntCount[msg.sender]++;
emit NewAnt(id, _name, _dna);
}
function createRandomAnt(string _name) {
require(ownerAntCount[msg.sender] == 0, "只能创建一只蚂蚁");
uint rand = uint(keccak256(_name));
uint randDna = rand % dnaModulus;
createAnt(_name, randDna);
}
function createHouse(string _houseName, uint _existGoods, uint _maxGoods) {
uint houseId = houses.push(House(_houseName, _existGoods, _maxGoods)) - 1;
houseToOwner[houseId] = msg.sender;
emit NewHouse(houseId, _houseName, _existGoods, _maxGoods);
}
function moveGoods(uint _antId, uint _originHouseId, uint _targetHouseId) moveCheck(_originHouseId, _targetHouseId) {
require(msg.sender == antToOwner[_antId], "只能用自己的蚂蚁搬东西");
require(msg.sender == houseToOwner[_originHouseId], "只能给自己家搬东西");
require(msg.sender == houseToOwner[_targetHouseId], "只能给自己家搬东西");
House storage originHouse = houses[_originHouseId];
House storage targetHouse = houses[_targetHouseId];
}
}
第二章 私有 / 公共函数
Solidity定义的函数的属性默认为公共。这就意味着任何一方 (或其它合约) 都可以调用你合约里的函数。
显然,不是什么时候都需要这样,而且这样的合约易于受到攻击。所以将自己的函数定义为私有是一个好的编程习惯,只有当你需要外部世界调用它时才将它设置为公共。
如何定义一个私有的函数呢?
uint[] numbers;
function _addToArray(uint _number) private {
numbers.push(_number);
}
这意味着只有我们合约中的其它函数才能够调用这个函数,给 numbers 数组添加新成员。
可以看到,在函数名字后面使用关键字 private 即可。和函数的参数类似,私有函数的名字用(_)起始。
实战
我们合约的函数 createAnt 的默认属性是公共的,这意味着任何一方都可以调用它去创建一只蚂蚁。 咱们来把它变成私有吧。
- 变 createAnt 为私有函数,不要忘记遵守命名的规矩哦!
- 我们修改了 createAnt 函数的名字,不要忘记修改调用的地方哦
- 我们还有几个函数是需要对外开放的,都给他们加上 public 吧。
pragma solidity ^0.4.20;
contract AntFamily {
event NewAnt(uint indexed antId, string name, uint dna);
event NewHouse(uint indexed houseId, string name, uint existGoods, uint maxGoods);
uint dnaDigits = 12;
uint dnaModulus = 10 ** dnaDigits;
struct Ant {
string name;
uint dna;
uint level;
uint moveCount;
}
struct House {
string name;
uint existGoods;
uint maxGoods;
}
Ant[] public ants;
House[] public houses;
mapping (uint => identity) public antToOwner;
mapping (identity => uint) ownerAntCount;
mapping (uint => identity) houseToOwner;
modifier moveCheck(uint _originHouseId, uint _targetHouseId) {
require(_originHouseId != _targetHouseId, "不能在同一个房子来回搬东西");
require(houses[_originHouseId].existGoods > 0, "没东西可以搬啦,换一个房子吧");
require(houses[_targetHouseId].maxGoods > houses[_targetHouseId].existGoods, "这个房子放满啦,换一个房子吧");
_;
}
function _createAnt(string _name, uint _dna) private {
uint id = ants.push(Ant(_name, _dna, 1, 0)) - 1;
antToOwner[id] = msg.sender;
ownerAntCount[msg.sender]++;
emit NewAnt(id, _name, _dna);
}
function createRandomAnt(string _name) public {
require(ownerAntCount[msg.sender] == 0, "只能创建一只蚂蚁");
uint rand = uint(keccak256(_name));
uint randDna = rand % dnaModulus;
_createAnt(_name, randDna);
}
function createHouse(string _houseName, uint _existGoods, uint _maxGoods) public {
uint houseId = houses.push(House(_houseName, _existGoods, _maxGoods)) - 1;
houseToOwner[houseId] = msg.sender;
emit NewHouse(houseId, _houseName, _existGoods, _maxGoods);
}
function moveGoods(uint _antId, uint _originHouseId, uint _targetHouseId) public moveCheck(_originHouseId, _targetHouseId) {
require(msg.sender == antToOwner[_antId], "只能用自己的蚂蚁搬东西");
require(msg.sender == houseToOwner[_originHouseId], "只能给自己家搬东西");
require(msg.sender == houseToOwner[_targetHouseId], "只能给自己家搬东西");
House storage originHouse = houses[_originHouseId];
House storage targetHouse = houses[_targetHouseId];
}
}
第三章 函数返回值
本章中我们将学习函数的返回值,要想函数返回一个数值,可以按如下定义:
string message = "hello world";
function sayHello() public returns (string) {
return message;
}
Solidity里,函数的定义里可包含返回值的数据类型(如本例中 string)。
实战
-
创建一个 private 函数,命名为 _prize。它只接收一个输入变量 _antId (类型 uint), 返回一个 bool 类型的结果,告知奖励是否发放成功。 -
我们的函数需要声明名为 myAnt 数据类型为Ant的本地变量(这是一个 storage 型的指针)。 将其值设定为在 ants 数组中索引为_antId 所指向的值。 -
增加 myAnt 的 moveCount。 -
创建一个 if 语句来检查 myAnt 的 moveCount 是否达到 5 的倍数。 -
如果以上条件为 true, 我们的蚂蚁就奖励升一级!所以: a. 增加 myAnt 的 level。 b. 返回 true。 -
添加一个 else 语句。 若没有达到 5 的倍数: a. 返回false。
pragma solidity ^0.4.20;
contract AntFamily {
event NewAnt(uint indexed antId, string name, uint dna);
event NewHouse(uint indexed houseId, string name, uint existGoods, uint maxGoods);
uint dnaDigits = 12;
uint dnaModulus = 10 ** dnaDigits;
struct Ant {
string name;
uint dna;
uint level;
uint moveCount;
}
struct House {
string name;
uint existGoods;
uint maxGoods;
}
Ant[] public ants;
House[] public houses;
mapping (uint => identity) public antToOwner;
mapping (identity => uint) ownerAntCount;
mapping (uint => identity) houseToOwner;
modifier moveCheck(uint _originHouseId, uint _targetHouseId) {
require(_originHouseId != _targetHouseId, "不能在同一个房子来回搬东西");
require(houses[_originHouseId].existGoods > 0, "没东西可以搬啦,换一个房子吧");
require(houses[_targetHouseId].maxGoods > houses[_targetHouseId].existGoods, "这个房子放满啦,换一个房子吧");
_;
}
function _createAnt(string _name, uint _dna) private {
uint id = ants.push(Ant(_name, _dna, 1, 0)) - 1;
antToOwner[id] = msg.sender;
ownerAntCount[msg.sender]++;
emit NewAnt(id, _name, _dna);
}
function createRandomAnt(string _name) public {
require(ownerAntCount[msg.sender] == 0, "只能创建一只蚂蚁");
uint rand = uint(keccak256(_name));
uint randDna = rand % dnaModulus;
_createAnt(_name, randDna);
}
function createHouse(string _houseName, uint _existGoods, uint _maxGoods) public {
uint houseId = houses.push(House(_houseName, _existGoods, _maxGoods)) - 1;
houseToOwner[houseId] = msg.sender;
emit NewHouse(houseId, _houseName, _existGoods, _maxGoods);
}
function moveGoods(uint _antId, uint _originHouseId, uint _targetHouseId) public moveCheck(_originHouseId, _targetHouseId) {
require(msg.sender == antToOwner[_antId], "只能用自己的蚂蚁搬东西");
require(msg.sender == houseToOwner[_originHouseId], "只能给自己家搬东西");
require(msg.sender == houseToOwner[_targetHouseId], "只能给自己家搬东西");
House storage originHouse = houses[_originHouseId];
House storage targetHouse = houses[_targetHouseId];
}
function _prize(uint _antId) private returns(bool) {
Ant storage myAnt = ants[_antId];
myAnt.moveCount++;
if (myAnt.moveCount % 5 == 0) {
myAnt.level++;
return true;
} else {
return false;
}
}
}
第四章 组合起来
让我们来学习如何把每次的搬家奖励同时赋予小蚂蚁吧。
实战
让我们把蚂蚁搬家和奖励发放组合起来,并且在奖励发放后触发一个事件。
-
定义一个 事件 叫做 MoveResult。 它有3个参数: antId (uint) 带indexed属性, originHouseId (uint), targetHouseId (uint), 和 prizeSuccess (bool)。 -
修改 moveGoods 函数在最后增加 a. 减少 originHouse 的 existGoods。 b. 增加 targetHouse 的 existGoods。 c. 调用刚才定义的 _prize 函数,使用一个 prizeSuccess (bool) 变量接收返回值。 d. 在调用完 _prize 函数后 生成事件 MoveResult。
pragma solidity ^0.4.20;
contract AntFamily {
event NewAnt(uint indexed antId, string name, uint dna);
event NewHouse(uint indexed houseId, string name, uint existGoods, uint maxGoods);
event MoveResult(uint indexed antId, uint originHouseId, uint targetHouseId, bool prizeSuccess);
uint dnaDigits = 12;
uint dnaModulus = 10 ** dnaDigits;
struct Ant {
string name;
uint dna;
uint level;
uint moveCount;
}
struct House {
string name;
uint existGoods;
uint maxGoods;
}
Ant[] public ants;
House[] public houses;
mapping (uint => identity) public antToOwner;
mapping (identity => uint) ownerAntCount;
mapping (uint => identity) houseToOwner;
modifier moveCheck(uint _originHouseId, uint _targetHouseId) {
require(_originHouseId != _targetHouseId, "不能在同一个房子来回搬东西");
require(houses[_originHouseId].existGoods > 0, "没东西可以搬啦,换一个房子吧");
require(houses[_targetHouseId].maxGoods > houses[_targetHouseId].existGoods, "这个房子放满啦,换一个房子吧");
_;
}
function _createAnt(string _name, uint _dna) private {
uint id = ants.push(Ant(_name, _dna, 1, 0)) - 1;
antToOwner[id] = msg.sender;
ownerAntCount[msg.sender]++;
emit NewAnt(id, _name, _dna);
}
function createRandomAnt(string _name) public {
require(ownerAntCount[msg.sender] == 0, "只能创建一只蚂蚁");
uint rand = uint(keccak256(_name));
uint randDna = rand % dnaModulus;
_createAnt(_name, randDna);
}
function createHouse(string _houseName, uint _existGoods, uint _maxGoods) public {
uint houseId = houses.push(House(_houseName, _existGoods, _maxGoods)) - 1;
houseToOwner[houseId] = msg.sender;
emit NewHouse(houseId, _houseName, _existGoods, _maxGoods);
}
function moveGoods(uint _antId, uint _originHouseId, uint _targetHouseId) public moveCheck(_originHouseId, _targetHouseId) {
require(msg.sender == antToOwner[_antId], "只能用自己的蚂蚁搬东西");
require(msg.sender == houseToOwner[_originHouseId], "只能给自己家搬东西");
require(msg.sender == houseToOwner[_targetHouseId], "只能给自己家搬东西");
House storage originHouse = houses[_originHouseId];
House storage targetHouse = houses[_targetHouseId];
originHouse.existGoods--;
targetHouse.existGoods++;
bool prizeSuccess = _prize(_antId);
emit MoveResult(_antId, _originHouseId, _targetHouseId, prizeSuccess);
}
function _prize(uint _antId) private returns(bool) {
Ant storage myAnt = ants[_antId];
myAnt.moveCount++;
if (myAnt.moveCount % 5 == 0) {
myAnt.level++;
return true;
} else {
return false;
}
}
}
|