文章目录

  • 第一章 生成蚂蚁的房子
    • 实战
  • 第二章 mapping+identity
    • Mapping(映射)
    • Identity(标识)
    • 实战
  • 第三章 msg.sender全局函数
    • 实战
  • 第四章 require
    • 实战
  • 第五章 增加一些属性
    • 实战
  • 第六章 Storage与Memory
    • 实战

接下来我们需要给我们的小蚂蚁建造一个属于他的小窝,让小蚂蚁有个自己的家。

第一章 生成蚂蚁的房子

小蚂蚁出生了,快来给它建造一所房子,给小蚂蚁一个安心的家。

实战

参照上面生成蚂蚁的例子,我们还需要给蚂蚁生成房子。

  1. 定义一个 事件 叫做 NewHouse。 它有4个参数: houseId (uint) 带indexed属性, name (string), existGoods (uint),和 maxGoods (uint)。
  2. 建立一个struct 命名为 House。
  3. 我们的 House 结构体有三个属性: name (类型为 string), existGoods (类型为 uint),和 maxGoods (类型为 uint)。
  4. 创建一个数据类型为 House 的结构体数组,用 public 修饰,命名为:houses。
  5. 建立一个函数,命名为createHouse。它有三个参数: _houseName (类型为string), _existGoods (类型为uint),和_maxGoods (类型为uint)。
  6. 参照蚂蚁的例子,定义房子id。 声明一个变量 houseId,数据类型是 uint。在下一行中把它用到 NewHouse 事件中。
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;}struct House {string name;uint existGoods;uint maxGoods;}Ant[] public ants;House[] public houses;function createAnt(string _name, uint _dna) {uint id = ants.push(Ant(_name, _dna)) - 1;emit NewAnt(id, _name, _dna);}function createRandomAnt(string _name) {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;emit NewHouse(houseId, _houseName, _existGoods, _maxGoods);}}

第二章 mapping+identity

Mapping(映射)

在之前的课程中,我们学习了结构体和数组 。映射是另一种在Solidity中存储数据的方法,使用关键字mapping来声明。

映射是这样定义的:

//可以用来通过userId 存储/查找的用户名
mapping (uint => string) userIdToName;

映射本质上是存储和查找数据所用的键-值对。我们可以直接通过键来查询该键对应的值。在上面的例子中,键是一个uint,值是一个 string。

注:映射中键必须是唯一的,而值是可以重复的。

Identity(标识)

蚂蚁链平台中,solidity合约使用identity替代官方solidity的address关键字。identity表示合约地址或账户地址,均为32字节,而官方solidity中address表示的地址是20字节。

//定义了一个identity类型
identity owner;

实战

为了存储蚂蚁和房子的所有权,我们会使用到三个映射:一个记录蚂蚁拥有者的地址,一个记录某地址所拥有蚂蚁的数量,最后一个记录房子拥有者的地址。

  1. 创建一个叫做 antToOwner 的映射。其键是一个uint(我们将根据它的 id 存储和查找蚂蚁),值为 identity。映射属性为public。
  2. 创建一个名为 ownerAntCount 的映射,其中键是 identity,值是 uint。
  3. 创建一个叫做 houseToOwner 的映射。其键是一个uint(我们将根据它的 id 存储和查找房子),值为 identity。
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;}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;function createAnt(string _name, uint _dna) {uint id = ants.push(Ant(_name, _dna)) - 1;emit NewAnt(id, _name, _dna);}function createRandomAnt(string _name) {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;emit NewHouse(houseId, _houseName, _existGoods, _maxGoods);}}

第三章 msg.sender全局函数

蚂蚁链平台提供一些全局接口函数可以直接使用,其中一个就是 msg.sender,它指的是交易的发送方,会返回一个identity类型。

以下是获取 msg.sender的一个例子:

function getSender() public returns (identity) {// 获取调用者return msg.sender;
}

注:类似的接口函数还有例如block.number获取当前块高,更多全局接口函数可以参考蚂蚁链开发者知识库。

实战

我们来修改 createAnt 方法 和 createHouse 方法,将蚂蚁和房子分配给函数调用者吧。

1.在得到新的蚂蚁 id 后,更新 antToOwner 映射,在 id 下面存入 msg.sender。

2.我们为这个 msg.sender 名下的 ownerAntCount 加 1。

跟在 JavaScript 中一样, 在 Solidity 中你也可以用 ++ 使 uint 递增。

uint number = 0;
number++;
// `number` 现在是 `1`了

3.在得到新的房子 id 后,更新 houseToOwner 映射,在 id 下面存入 msg.sender。修改三行代码即可。

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;}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;function createAnt(string _name, uint _dna) {uint id = ants.push(Ant(_name, _dna)) - 1;antToOwner[id] = msg.sender;ownerAntCount[msg.sender]++;emit NewAnt(id, _name, _dna);}function createRandomAnt(string _name) {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);}}

第四章 require

在之前的章节中,我们已经让用户通过调用createRandomAnt函数来创建新的蚂蚁。但是,如果用户能持续调用这个函数来创建出无限多个蚂蚁,这游戏就太没意思了!

于是,我们作出以下限定:每个玩家只能调用一次这个函数。 这样一来,新玩家可以在刚开始玩游戏时通过调用它为其创建初始蚂蚁。

我们怎样才能限定每个玩家只能调用一次这个函数呢?

答案是使用require。 require使得函数在执行过程中,当不满足某些条件时抛出错误,并停止执行:

function sayHiToBob(string _name) public returns (string) {// 比较 _name 是否等于 "Bob". 如果不成立,抛出异常并终止程序require(keccak256(_name) == keccak256("Bob"));// 如果返回 true, 运行如下语句return "Hi!";
}

如果你这样调用函数 sayHiToBob(“Bob”),它会返回“Hi!”。而如果你调用的时候使用了其他参数,它则会抛出错误并停止执行。

因此,在调用一个函数之前,用 require 验证前置条件是非常有必要的。

实战

在我们的蚂蚁搬家游戏中,我们不希望用户通过反复调用 createRandomAnt 来创建无限多个蚂蚁 。

我们使用 require 来确保这个函数只有在每个用户第一次调用它的时候执行,用以创建唯一的一个蚂蚁。

  1. 在 createRandomAnt 的前面放置 require 语句。 使得函数先检查 ownerAntCount [msg.sender] 的值为 0 ,不然就抛出一个错误,输出错误信息为 “只能创建一只蚂蚁”。

注意:在 Solidity 中,关键词放置的顺序并不重要

  • 虽然参数的两个位置是等效的。 但是,由于我们的答案检查器比较呆板,它只能认定其中一个为正确答案
  • 于是在这里,我们就约定把ownerAntCount [msg.sender]放前面吧
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;}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;function createAnt(string _name, uint _dna) {uint id = ants.push(Ant(_name, _dna)) - 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);}}

第五章 增加一些属性

我们的小蚂蚁已经有 name 和 DNA 的属性了,现在让我们给它增加一些新的属性吧。

实战

我们来给蚂蚁添2个新功能:level 和 moveCount。

  1. 为 Ant 结构体 添加两个属性:level(uint32)和 moveCount (uint32) 。因为希望同类型数据打成一个包,所以把它们放在结构体的末尾。
  2. 因为我们给 Ant 结构体中添加 level 和 moveCount 两个参数,现在创建一个新的 Ant 结构体时,需要修改 createAnt(),在其中把新旧参数都初始化一下。

修改 ants.push 那一行, 添加加2个参数:1(表示当前的 level )0(表示当前搬运的次数)。

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;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);}}

第六章 Storage与Memory

在Solidity中,有两个地方可以存储变量 —— storage 或 memory。

storage变量是指永久存储在区块链中的变量。 memory变量则是临时的,当外部函数对某合约调用完成时,memory变量即被移除。 你可以把它想象成存储在你电脑的硬盘或是RAM中数据的关系。

大多数时候你都用不到这些关键字,默认情况下Solidity会自动处理它们。 状态变量(在函数之外声明的变量)默认为storage形式,并永久写入区块链;而在函数内部声明的变量是memory型的,它们会在函数调用结束后消失。

然而也有一些情况下,你需要手动声明存储类型,主要用于处理函数内的结构体和数组时:

contract BookStore {struct Book {string name;string status;}Book[] books;function buyBook(uint _index) public {// Book myBook = books[_index];// 上面的例子看上去很直接,不过Solidity将会给出警告// 告诉你在这里应该明确定义 `storage` 或者 `memory`。// 所以你应该明确定义 `storage`:Book storage myBook = books[_index];myBook.status = "solded";// 这将永久把 `books[_index]` 变为区块链上的存储// 如果你只想要一个副本,可以使用`memory`:Book memory anotherBook = books[_index + 1];// 这样 `anotherBook` 就仅仅是一个内存里的副本了anotherBook.status = "solded!";// 这样仅仅修改临时变量,对 `books[_index + 1]` 没有任何影响// 不过你可以这样做:books[_index + 1] = anotherBook;// 如果你想把副本的改动保存回区块链存储}
}

如果你还没有完全理解究竟应该使用哪一个,也不用担心 —— 在本教程中,我们将告诉你何时使用 storage 或是 memory,并且当你不得不使用到这些关键字的时候,Solidity 编译器也发警示提醒你的。

现在,只要知道在某些场合下也需要你显式地声明 storage 或 memory就够了!

实战

  1. 创建一个名为 moveGoods 的函数。 使用三个参数:_antId( uint类型 )_originHouseId( uint类型 ) 和_targetHouseId (也是 uint 类型)。
  2. 我们不希望别人用我们的蚂蚁去搬家。 首先,我们确保对自己蚂蚁的所有权。 通过添加一个require 语句来确保 msg.sender 只能是这个蚂蚁的主人(类似于我们在 createRandomAnt 函数中做过的那样),如果不是这个蚂蚁的主人就抛出一个错误,输出错误信息为 “只能用自己的蚂蚁搬东西”。

注意:同样,因为我们的答案检查器比较呆萌,只认识把 msg.sender 放在前面的答案,如果你切换了参数的顺序,它就不认得了。 但你正常编码时,如何安排参数顺序都是正确的。

  1. 同理,我们不希望把东西搬去别人的房子,也不希望把别人房子的东西搬到我们房子里,所以我们也需要确保对自己房子的所有权。与上一步类似,添加一个require 语句来确保 msg.sender 只能是两个房子的主人,如果不是自己的房子就抛出一个错误,输出错误信息为 “只能给自己家搬东西”。
  2. 为了获取两个房子的已经搬过的物品数量,我们的函数需要声明名为 originHouse 和 targetHouse 数据类型都为House的本地变量(这是一个 storage 型的指针)。 将其值设定为在 houses 数组中索引为_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;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) {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];}}

【蚂蚁链学习3】蚂蚁搬家(蚂蚁链智能合约实战solidity)相关推荐

  1. 区块链学习(8) EOS环境安装和智能合约部署实战(绝对干货!)

    硬件配置 操作系统要求 1. Amazon 2017.09 and higher 2. Centos 7 3. Fedora 25 and higher (Fedora 27 recommended) ...

  2. 智能合约实战 solidity 语法学习 10 [ BNB合约解读 ] 附代码

    目录 1. ether官网搜索BNB 2.获取BNB合约源码 3. BNB合约源码解读 1. ether官网搜索BNB https://etherscan.io/address/0xB8c77482e ...

  3. 区块链技术进阶-深入详解以太坊智能合约语言 solidity(含源码)-熊丽兵-专题视频课程...

    区块链技术进阶-深入详解以太坊智能合约语言 solidity(含源码)-103人已学习 课程介绍         区块链开发技术进阶-深入详解以太坊智能合约语言 solidity视频培训教程:本课程是 ...

  4. 区块链技术最佳的监管方式是智能合约监管智能合约

    区块链最新消息 1.在国内首部<区块链安全生存指南>发布 最新消息:比特大.长亭科技和ConsenSys陆联手发布了全国首部<区块链安全生存指南>.该指南围绕区块链技术安全,对 ...

  5. 区块链学习笔记:DAY01 区块链的技术原理

    其实很早之前就听过区块链,也看过有关区块链的介绍,那个时候的理解主要还是一句话:分布式记账 然后开始关注比特币,听了有几年了,对于其来历.用途其实一直都是一知半解. 这次的课算是第一次以一个学员的身份 ...

  6. 区块链学习笔记:D02 区块链的技术发展历史和趋势

    对于区块链的技术发展历史,其实在我的印象中也就对比特币有所了解,也听过什么火币之类的玩意,但是具体是什么.怎么运作的就不清楚了... 这次的内容首先是讲解了区块链的技术演进,一张图一目了然,虽然里面涉 ...

  7. 区块链学习笔记(初识区块链)

    区块链 区块链比比特币更加迷人,区块链具有革命性的意义,它是一个创造信任的机器 AI是生产力的提升,区块链是生产关系的变更,可能使人类的生产结构产生重大变革,淡化国家的权利机构 比特币按字节数收费 一 ...

  8. 10本区块链热门图书(应用开发、智能合约等)免费送!

    欢迎访问网易云社区,了解更多网易技术产品运营经验. "互联网之后就是区块链时代,区块链是实现未来跟踪经济的关键技术."世界上真的存在 100% 去中心化的系统吗?区块链到底是什么? ...

  9. 区块链开发入门:基于以太坊智能合约构建 ICO DApp

    写给前端开发者的第一本区块链开发入门指南,通过从 0 到 1 实战开发一个 ICO DApp 项目(基于 V 神的 DAICO 设计思想),深入掌握区块链及以太坊技术. 为什么要选择区块链开发? 未来 ...

最新文章

  1. matlab绘制y3=e (-x),MATLAB图形绘制-二维配套实验
  2. Mac 完全卸载 Java
  3. Typecho浅色三栏主题
  4. Oculus首席科学家发声,称VR需要能够“欺骗”大脑
  5. 存储过程和函数的区别?
  6. sass编译css(转自阮一峰)
  7. 【声传播】——多层介质平面波的反射问题
  8. 三星关闭在华最后一家电脑厂 约850名员工受影响
  9. greys的简单使用
  10. Chrome浏览器提示您的连接不是私密连接解决办法
  11. 微机原理与接口技术实验:完整的汇编语言程序设计实验
  12. linux驱动开发学习笔记十六:gpio相关OF函数和子系统API函数
  13. autoconfig
  14. Java实验14 计算多个二维图形的面积之和
  15. 前端学习笔记 - promise是什么?能解决什么问题?
  16. JAVA实现 PDF转换 常用工具类(html转PDF、PDF添加页码、PDF文件下载、PDF添加印章或者水印)
  17. 基础5·BMI计算(if分支结构)
  18. 世间皆苦,看完这些图,我们还是要笑对生活!
  19. Kafka 压缩、限流和 SASL_PLAIN 、 SASL_SCRAM-SHA-256简单认证
  20. 数据库课程设计--公交车管理系统总结(Qt学习小结)

热门文章

  1. 【图解SHA1杂凑算法】SHA1杂凑算法的Python实现保姆级教程 | 物联网安全 | 信息安全
  2. SketchUp的自动化探索 (二)建筑表皮生成器
  3. 【小程序,h5页面】 踩坑之路
  4. 关于设计(三)设计存在的意义
  5. java基础梳理--朝花夕拾(二)
  6. 我的第一个油猴脚本「屏蔽CSDN底下的登录栏」
  7. 不知名菜鸟的day12
  8. 《C陷阱和缺陷》中两个有趣的代码
  9. 【面试】 瓜子二手车 Java 方向面经
  10. 关于java收费该如何