cryptokitties(以太坊云养猫)是近期出现在以太坊区块链上一个游戏。超级可爱的猫形象,再配合上繁殖,配种,拍卖这样丰富的玩法,一下子就引爆了以太坊的区块链。这款游戏的核心是基于以太坊的智能合约,也是第一款基于智能合约的游戏。这款游戏除了繁殖时候的基因工程合约以外,其他合约都是开源的,她的源码对于学习智能合约的编写有着很大的启发意义。下面是我的一点点对于智能合约和cryptokitties源码的理解,分享给大家。 (本文假设你已经有一定的编程经验,而且对cryptokitties游戏规则有一定的了解) 我配出来的小暹罗,仅售0.3ETH,欢迎联系 智能合约简述
更多的细节可以阅读以太坊官网。 Ownable合约这是一个很简单的智能合约,定义了所有权,她的作用类似一个抽象的基类,后续的类通过继承她来简化代码。 modifier也是以太的关键字,定义一个修饰符,她写在函数最后,在函数执行前,先执行修饰符中代码。一般修饰符代码都是权限检查等等。 就比如这个代码,用来移交合约的所有权,onlyOwner修饰符会先执行,因为只有真正拥有这个合约的人,才有权利移交这个合约。 ERC721合约ERC是以太坊代币标准的缩写,ERC721是以太坊区块链代表标准的一种。比较常见的是ERC20。这个标准相当于定义了一个接口,当你的合约实现了这个接口以后,钱包软件就能通过调用这些接口,监听事件等等操作你的ERC721代币。用其他编程语言来理解,比如JAVA的STRUTS框架,它定义了action接口,你通过实现action接口,接入到struts这个框架中。又或者LINUX开发中,你要写一个驱动,你必须实现file_operations 里的各种读写函数。 那ERC721代币和ERC20代币有什么不同? ERC721代币的核心是“Non-Fungible Tokens”,不可互换的代币。怎么理解“不可互换”? 比如你有2只猫(猫A和猫B),你的代币数量就是2,但是猫A和猫B是不同的,当你卖出你的猫时,你必须指定是卖哪知猫,因为猫A和猫B是不可以互换的。类比ERC20,就好比你有2块钱,这两块钱,你花其中任意一块钱,都不影响结果,只要账户里扣一块钱就可以了。 ERC721每个代币都有一个独立唯一的tokenid,这个id在这个cryptokitties里就是猫的id,比如下面这个链接“103646”其实就是猫的tokenid。
从这个transfer函数也能看出,每次转账,只能转某个具体的猫(没有数量)。 GeneScienceInterface合约这个合约没有源码,从名字看已经很明显,就是猫的基因工程,用于决定新出生猫的基因。这里仅仅定义了一个接口,没有具体实现,后续的调用请看后续代码分析。 KittyAccessControl合约require关键字(函数),它类似很多编程语言里的assert,你如果括号里的条件是False,它会终止合约的执行。 相当于 if state == false: exit(),以前以太坊退出程序的时候会收走你左右的手续费,现在硬分升级以后,仅仅收取运行到这里的手续费,这样大大节约了手续费的开支。 这个合约也是一个类似抽象基类合约,定义了猫的权限控制,其中定义了3个角色,CEO,CFO,COO。 定义了一些修饰符,比如 onlyCEO只能CEO操作的方法,onlyCFO就是只能CFO操作的方法。 游戏本身可以暂停 游戏运营方可以暂停游戏,后面很多游戏的方法都有whenNotPaused修饰符,这个修饰符以一种很简洁的方式处理了游戏的暂停,避免了大量的代码重复。 上面写了一大堆终于要进入cryptokitties的正题。 KittyBase合约首先它是继承KittyAccessControl,这样它就可以直接使用之前上面定义的一堆控制修饰符。然后看看两个事件: Birth事件 外部程序通过关注该事件,当有猫出生的时候,就能从区块链上得到通知。cryptokitties的外部程序关注了这个事件,然后在有新猫出生的时候,给你发送邮件。Birth事件上的参数,是给外部使用的,区块链会告诉你 谁owner,获得了一直新猫(kittyId),猫的父亲母亲是谁(sireId,matronId),基因genes(是什么) Transfer事件用于通知猫的转手。 猫的核心数据结构猫的数据结构 Kitty结构是存储在区块链上猫的数据表示。cryptokitties也就是通过操作这个数据结构的属性变化,来完成各种猫相关的业务。 generation 猫的代数,系统生成的猫是 Gen 0表示第0代。后面的后代,代数会慢慢增长。 genes 猫的基因,决定了猫的样子,但是需要注意的是,存储在以太坊区块链上的仅仅是一个基因数据,具体的显示,本身还是由cryptokitties服务器提供的数据,也就是说,如果cryptokitties关了服务器,你的猫只是这个256位数。 birthTime出生时间,这个属性用处不大 cooldownEndBlock 还有多久可以配种,以太坊上,有关时间的操作,大多数是用区块表示的。比如当年区块号是1000,出1个块时间是15秒,那么我说,这个猫要等1分钟以后可以配种,那么其实就是要等4区块(60秒/15)以后,就是1004号区块,在1005号出来的时候,你才能进行配种。 matronId,sireId matronId表示这只猫的猫妈妈的ID,sireId表示猫爸爸ID。由于初代猫是没有猫爸爸猫妈妈(是开发团队变出来的)所以初代ID都为0。 siringWithId 配偶ID,这个游戏里,猫是没性别的,当你参与交配的时候,扮演母猫的一方拿走出生的小猫,扮演工贸的一方拿走配种费。siringWithId !=0表示这只猫正在配种,siringWithId==0表示这种猫空巢。 cooldownIndex 配种冷却时间 当年点击一只猫的时候,这个属性决定了它的剩余速度级别。 它和下面这张表是配合使用的 cooldownIndex = 0代表1分钟就是上图的FAST,1代表SWIFT。 下面的代码定义了,猫业务核心数据。 mapping是以太坊智能合约的一种数据结构,它和JAVA里的hashmap,python里的dict是一样的,就是一张hash表。这张hash表是存储在以太坊智能合约上的。
kittyIndexToOwner表,通过这个映射,可以快速的知道,哪知猫是谁的。那为什么owner属性为什么不放在Kitty的数据结构里呢?笔者推测,很多场景并不需要获取Kitty上具体数据,如果每次要先找到猫再获取主人,效率可能比较低,毕竟智能合约的执行是要钱的。 ownershipTokenCount表,记录了谁有几只猫。 kittyIndexToApproved表,记录了一种授权操作,你可以把你的猫授权给某个地址,比如你进行拍卖的时候,你的猫其实授权给了拍卖合约,这个合约有权利将你的猫转给他人。 sireAllowedToAddress表,你可以把你的猫拿去配种,对方要和你猫的配种,必须要得到你的授权,那这张表记录了,你把你的那种猫允许谁配种。 _transfer函数 这个函数描述了猫转让的核心操作。 _from 是原持有人,_to是你要转让给谁,tokenId表示什么猫。首先对方猫数量要增加1,第二修改kittyIndexToOwner里该猫资产的所有人。这个函数里还对于from地址是否为0进行判断,因为这个猫可能是系统创建的,那from就是0。如果from是一个用户,那他的猫数量要减1,然后清除相关配种权力和转让权力。最后通过 Transfer(_from, _to, _tokenId); 发送猫转让事件,让外部系统进行通知等等。 _createKitty创建一只猫的核心函数的,首先比较有趣的是 cooldownIndex的处理,也就是说新的猫的繁殖速度是和他是第几代猫直接相关,越早代的猫繁殖冷却事件越低。然后新创建的Kitty对象会被放入Kitty数组,同时获得其KittyId。同时发送Birth事件,系统通过监听这个事件,给你发送 Your Kitty is born邮件。同时调用_transfer内部函数,分配这只猫的所有权,因为并不是用户和用户之间转让,所以_from是0表示系统转让给你。 KittyOwnership合约上面讲的好几个合约,其实都是抽象基类,并没有部署在以太坊上,他们都是为了节约代码抽出来的。KittyOwnership合约是一个真正部署在以太坊上的合约,他定义了一个 ERC721代币。KittyOwnership把KittyBase和ERC721的接口桥接起来了。里面的很多代码,基本都是KittyBase合约的接口转换,并没有什么很特别的逻辑,大家可以自行阅读。 就挑这个方法讲一下,这个方法描述了你有几只猫,他遍历整个目前所有的猫,然后在kittyIndexToOwner查看这只猫是不是你的,因为之前记录了你有几只猫,所以查找到你所有的猫后就可以停下而不用遍历完所有的猫集合。但是这个函数依然是很昂贵的,注释的作者写到,这个函数不应该被智能合约调用,这个函数应该是本地用来查找区块链用的。他的效果就是网页的My Kitties按钮。 笔者写到这里已经虚脱,猫的繁殖和拍卖,下期再讲了,欢迎喜欢编程和区块链的朋友关注我。 |
跟着cryptokitties(以太坊云养猫)学写智能合约(上)
最新推荐文章于 2024-06-10 12:49:29 发布