本文来自作者 崔广斌 在 GitChat 上分享 「玩区块链游戏谜恋猫 CryptoKitties, 学习区块链技术赚 ETH」
编辑 | Mc Jin
便于读者更清晰阅读本文,先列出本文的内容大纲:
1.以太坊开发技术基础
2. 谜恋猫系统结构
3. 谜恋猫游戏规则
4. 使用 web3js 读写区块链上的谜恋猫数据
5. 使用 api.cryptokitties.co 读取数据
6. 通过程序赚 ETH
7. 谜恋猫的一些统计数据
8. 玩谜恋猫的一些启发
9. 开发时遇到问题怎么办?
以太坊是可编程的区块链,是业内公认的区块链 2.0 代表项目。可以将以太坊理解为一个操作系统,使用 Solidity 等语言编写智能合约发布应用到链上,使用 Go、Java、Python、JavaScript 等语言在链下调用链上的智能合约读写区块链数据,通过这种方式实现各种各样的区块链应用。
比特币的总上限是2100万,而以太坊的内置代币以太币(Ether)没有确切的总量上限。目前以太坊大概每15秒出一个新块,一个新块奖励矿工 3 ETH 。
以太坊的设计者认为随着时间流逝总会发生因为粗心和死亡等原因带来的币的遗失,假设币的遗失是每年货币供应量的一个固定比例,则最终总的流通中的货币供应量会稳定在一个等于年货币发行量除以遗失率的值上,使得供应量会趋于稳定。
比特币缺少图灵完备性,尽管比特币脚本语言可以支持多种计算,但是它不能支持所有的计算,如不支持 for 循环。以太坊是准图灵完备的,之所以增加“准”,是因为智能合约在以太坊区块链上执行时是受限的。
在以太坊区块链上执行交易(转账、调用智能合约)需要消耗 Gas ,一般来说操作步骤越复杂需要的 Gas 越多,而一个块有 Gas 上限(目前约为 800万)。向普通账户做1次转账操作目前消耗 2.1 万 Gas。
在块 Gas 上限为 800 万时,假设调用一个智能合约中某个函数时会向400个账户转账,因为会至少消耗 400 * 2.1 万 = 820 万 Gas,超出块的 Gas 上限 800 万,合约调用会失败。
关于以太坊更详细的介绍可以参考:以太坊白皮书和以太坊黄皮书。
Solidity 是一种在语法上类似 JavaScript 的高级语言,编译 Solidity 代码可以生成以太坊虚拟机代码。
Solidity 是静态类型语言,支持继承、库和复杂的用户定义类型等特性。
使用 Solidity 可以很容易创建投票、众筹、封闭拍卖、多重签名钱包、谜恋猫游戏之类的智能合约。
由于以太坊区块链的限制,在链上无法读取链下数据,使用 Solidity 你也无法来调用传统的 API,例如你无法调用某天气网站提供的天气 API。
另外在以太坊区块链上,无法让程序在指定时间自动运行。
谜恋猫游戏为了真实性,在猫怀孕后不是立即生出小猫,而是需要在满足一定条件后由外部来触发生猫函数为猫提供接生服务,一些开发者根据游戏的这个特性还可以赚猫的接生费,后文会有详细说明。
etherscan.io 提供了验证程序源码的服务。原理是使用公开的代码及指定的编译器版本再编译一次程序。
然后和发布到区块链的以太坊的二进制代码做比对,如果一致说明公开的代码就是在区块链上运行的那份代码。
下图是一份通过验证的代码截图。
可以访问这里,进一步了解 Solidity。
ERC-20 和 ERC-721 都是以太坊 EIP(Ethereum Improvement Proposal,以太坊改进协议),更多 EIP 见这里。
ERC-20 定义了一份代币标准,可以理解为定义了一个接口类,在实现具体的 ERC-20 代币时,要给出各个接口的具体实现,如获取代币名称、代币符号、总供应量、小数位数、转账等。
使用 ERC-20,可以大幅度降低发币成本,发币方无需开发钱包和区块链浏览器,交易所也可以轻松支持新的代币充值提现等操作。
ERC-20 币是同质的,你的一个币和我的一个币是等价的。ERC-721 是非同质代币(Non-fungible Tokens),每个 ERC-721 有唯一的 ID,转账时,不再是转多少币,而是转某个tokenId,如 transferFrom:
谜恋猫实现了 ERC-721 规范,每个猫有一个唯一的 ID,玩家花 ETH 买猫时,智能合约会调用 transferFrom 修改猫的所有者。
我使用了以太坊 Go 客户端搭建的全节点,参考文档见(https://github.com/ethereum/go-ethereum),几个注意事项如下:
电脑配置不能太低。我刚开始使用的是阿里云1核 CPU、2500 MHz 的 ECS,发现怎么也同步不到最新块,升级到了4核后同步正常了;
第一次同步时使用 - -fast 选项,可以更快地同步到最新块,目前(2018-04-02)使用 - -fast 选项同步到最新块后预计占用 60G 空间;
geth 运行时间长了可能会有问题,我使用守护进程启动 geth,每 4 个小时 kill 掉 geth,目前基本上能一直同步到最新块;
硬盘空间要足够大,建议至少1T以上。我3个月前使用 - -fast 同步完成后才占 40 多 G 空间,之后正常模式同步后硬盘占用空间快速增长,运行3个月左右已经 500G 了。
为了更方便、更快速的调用相关 API,建议在本地服务器上搭建一个以太坊全节点,并保持同步到最新区块高度。
如果想通过程序买卖猫但又不想自己搭建全节点,可以使用 MetaMask 的节点,API 地址请访问(mainnet.infura.io/metamask),使用 MetaMask 的节点 API,监听事件可能会受到影响,一个方法是遍历某个区块的所有交易信息,然后选择自己关注的交易信息再做相关处理。
如果你还没有在 www.cryptokitties.co 买过猫,建议先买只猫后再阅读后续内容。一些入门攻略见(http://www.maomao123.co/faq),遇到问题可通过谜恋猫 QQ 群(728507998)咨询。
谜恋猫是一个 DApp(Decentralized Application),部分程序部署在区块链上,部分程序部署在中心化的服务器上,总体架构如下图:
谜恋猫在以太坊区块链上一共有4个智能合约:
CryptoKittiesCore :核心代码,已开源,2016行代码;
CryptoKittiesSalesAuction :猫拍卖机制,已开源,604行代码;
CryptoKittiesSiringAuction :配种拍卖机制,已开源,589行代码;
geneScience :基因工程,未开源。
以玩家挂单卖猫为例,说明相关步骤:
玩家在自己某只猫的页面点击出售按钮,设置出售价格,通过 MetaMask 钱包操作后等待(页面上猫的状态是非在售;MetaMask 交易状态是 pending 中);
MetaMask 将交易广播出去(页面上猫的状态是非在售;MetaMask 交易状态是 pending中);
交易被矿工打包到区块中(页面上猫的状态是非在售;MetaMask 交易状态是 pending中);
谜恋猫的以太坊节点监听到卖猫事件,更新数据库中猫的信息、更新缓存数据、更新搜索索引数据;MetaMask 的以太坊节点更新交易状态(页面上猫的状态是在售;MetaMask 交易状态是已完成);
用户卖猫后,在页面上不是立即可以看到猫处于在售中,而是需要等待一会儿才能看到,整个过程是异步的。
这也为懂技术的玩家留下机会,可以在猫刚开卖还未在页面上显示出售中就能立即买下猫,详细方法见后文。
一般涉及到用户数据的网站或 App,都会要用户注册帐号,哪怕是使用第三方授权。
而对于谜恋猫游戏,即使你之前从未访问过网站,只要有你的以太坊钱包地址,其他人就可以给你送猫。
安装 MetaMask 插件后第一次登陆网站,需要你按提示点击一个消息签名按钮,以便验证当前用户身份。
原理是椭圆曲线加密算法,相关函数为 web3.eth.sign(address, dataToSign)、ecRecover,具体用法可以搜索下。
在谜恋猫页面,点击顶部导航“猫市”,默认就是待收养的猫猫。点击“筛选猫咪”可以通过多种方式做筛选,找到自己的目标猫咪。初期玩建议按价格从低到高排序,选两三只便宜的猫咪。
搜索功能并不是直接从区块链读取数据的,而是通过同步区块链数据后在中心化服务器中建立的索引。
点击一只猫咪后,进入单个猫咪页面,再点击“立即购买”就可以买猫了。
在点击“立即购买”按钮时,会调用 web3js,触发弹出 MetaMask 插件窗口。MetaMask 插件中会显示 Amount(转账额度)、GasLimit(燃料上限)、GasPrice(燃料价格)等参数,部分数据做了隐藏,如购买猫调用的是 bid(uint256 _tokenId) 函数。
点击“submit”按钮后,MetaMask 会将交易数据提交到以太坊网络,等待矿工打包确认,当矿工将交易数据打包到某个区块中才算真正完成相关函数执行。
但成功打包到区块中不一定能成功买到猫,因为交易过程是异步的,在这个过程中也可能有其他人购买同一只猫,如果他人的交易比你的交易优先被矿工打包,你就买不到猫了。另外,卖家也可以取消卖猫。
在此简单说下以太坊的 Gas 机制,在以太坊中涉及到任何写数据的操作都有消耗 Gas,矿工打包一般优先打包 gasPrice 高的交易。
gas * gasPrice 表示在操作过程中消耗的以太币,剩余的以太币 (gasLimit - gas)* gasPrice 会返回给用户。
一般来做操作越复杂,消耗的 Gas 越多,占用的存储空间越大,消耗的 Gas 越多。
你买卖达成后后,平台会收取交易的 3.75% 作为手续费。因以太坊区块链上的数据对所有可见,卖猫不支持暗拍(备注: ENS支持安排,但相对复杂,不合适面向普通用户)。
为了让买家以相对和合适的价格找到相对合适的卖家,卖猫时,可以设置起始价、结束价、价格变化时间段(出售期限),价格变化时间段结束后,还会处于出售状态,价格保持为结束价。
当前价格的计算方法:
/// @dev Computes the current price of an auction. Factored out /// from _currentPrice so we can run extensive unit tests. /// When testing, make this function public and turn on /// `Current price computation` test suite. function _computeCurrentPrice( uint256 _startingPrice, uint256 _endingPrice, uint256 _duration, uint256 _secondsPassed ) internal pure returns (uint256) { // NOTE: We don't use SafeMath (or similar) in this function because // all of our public functions carefully cap the maximum values for // time (at 64-bits) and currency (at 128-bits). _duration is // also known to be non-zero (see the require() statement in // _addAuction()) if (_secondsPassed >= _duration) { // We've reached the end of the dynamic pricing portion // of the auction, just return the end price. return _endingPrice; } else { // Starting price can be higher than ending price (and often is!), so // this delta can be negative. int256 totalPriceChange = int256(_endingPrice) - int256(_startingPrice); // This multiplication can't overflow, _secondsPassed will easily fit within // 64-bits, and totalPriceChange will easily fit within 128-bits, their product // will always fit within 256-bits. int256 currentPriceChange = totalPriceChange * int256(_secondsPassed) / int256(_duration); // currentPriceChange can be negative, but if so, will have a magnitude // less that _startingPrice. Thus, this result will always end up positive. int256 currentPrice = int256(_startingPrice) + currentPriceChange; return uint256(currentPrice); } }
出租种猫和卖猫类似,可以通过出租种猫赚钱,但所有权还是原主人的。相关合约见 CryptoKittiesSiringAuction 。
生猫规则如下:
任意猫都可以充当爸爸或妈妈的角色;
交配时不能乱伦;
每生育一次,恢复时间变长,直到需要7天时间恢复;
孕期 = 怀孕后妈妈的恢复时间,想尽快生出小猫的话,应该使用休息时间短的猫做妈妈;
小猫代数 = max(爸爸的代数,妈妈的代数) + 1;
小猫恢复时间 cooldown_index = min( 小猫代数 / 2 ,13);
不同 cooldonw_index 对应的时间见下面内容。
生猫函数如下:
.....
扫码阅读全文
并与作者交流
近期热文
《区块链在哪些案例上发挥着重大作用》
《区块链落地中的九大问题与解法》
《比特币和区块链基础》
《穷人入门区块链指南》