【蚂蚁链学习4】授予勋章(蚂蚁链智能合约+函数部分应用+事件event)

第一章 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 中的代码。

实战
  1. 创建一个名为 moveCheck 的modifier,它接收2个参数, _originHouseId (uint类型) 以及 _targetHouseId (uint类型)。
  2. 运用函数逻辑确保蚂蚁搬家的两个房子 是不同的房子,_originHouseId 和 _targetHouseId 不能相同,如果一样就抛出一个错误,输出错误信息为 “不能在同一个房子来回搬东西”。
  3. 还需要确保蚂蚁搬家的原房子 houses[_originHouseId].existGoods 大于0,如果不大于0就抛出一个错误,输出错误信息为 “没东西可以搬啦,换一个房子吧”。
  4. 最后要确保蚂蚁搬家的目标房子 houses[_targetHouseId].maxGoods 大于 houses[_targetHouseId].existGoods,如果不超过就抛出一个错误,输出错误信息为 “这个房子放满啦,换一个房子吧”。
  5. 记住,修饰符的最后一行为 _;,表示修饰符调用结束后返回,并执行调用函数余下的部分。
  6. 修改 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 的默认属性是公共的,这意味着任何一方都可以调用它去创建一只蚂蚁。 咱们来把它变成私有吧。

  1. 变 createAnt 为私有函数,不要忘记遵守命名的规矩哦!
  2. 我们修改了 createAnt 函数的名字,不要忘记修改调用的地方哦
  3. 我们还有几个函数是需要对外开放的,都给他们加上 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)。

实战
  1. 创建一个 private 函数,命名为 _prize。它只接收一个输入变量 _antId (类型 uint), 返回一个 bool 类型的结果,告知奖励是否发放成功。

  2. 我们的函数需要声明名为 myAnt 数据类型为Ant的本地变量(这是一个 storage 型的指针)。 将其值设定为在 ants 数组中索引为_antId 所指向的值。

  3. 增加 myAnt 的 moveCount。

  4. 创建一个 if 语句来检查 myAnt 的 moveCount 是否达到 5 的倍数。

  5. 如果以上条件为 true, 我们的蚂蚁就奖励升一级!所以:

    a. 增加 myAnt 的 level。

    b. 返回 true。

  6. 添加一个 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;
    }
  }
  
}

第四章 组合起来

让我们来学习如何把每次的搬家奖励同时赋予小蚂蚁吧。

实战

让我们把蚂蚁搬家和奖励发放组合起来,并且在奖励发放后触发一个事件。

  1. 定义一个 事件 叫做 MoveResult。 它有3个参数: antId (uint) 带indexed属性, originHouseId (uint), targetHouseId (uint), 和 prizeSuccess (bool)。

  2. 修改 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;
    }
  }
  
}
  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小天才才

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值