原创 by 七镜@无退社区编辑部
原文
永久地址
以太猫启示录—动态修改合约代码
区块链有一个不得不说的特性,那就是不可篡改。只要一段数据被写到了区块链上,几乎就没有什么可能再修改这段数据。以太坊到智能合约虽然是代码,但本质上也是一段数据。所以当智能合约被部署到区块链上,也就不再能够修改。这是区块链的一个优秀的特性,在这种情况,但一个严肃的合约被上链之后,也就天生具备了可信任的特点。
但是这却和软件开发相悖,要知道软件开发通常是需要进行大量的修改。作为 DApps,更多的应该把他们视为一种应用。所以就可能会有需要修改 bug,也可能需要添加了新的功能等等。在第一篇文章中我们讲到了两个方法,一是销毁一个合约,二是暂停一个合约。这篇文章我们来看下以太猫中的第三种方法,如何在不销毁合约的情况下,动态的修改 DApps 的行为。
以太猫有一个重要的功能,就是使用现有的以太猫养育下一代。这和以前的宠物养成游戏基本一致。从某种意义上来说,这个养成算法是整个以太猫最有价值的部分。什么样的父母应该养育出什么样的下一代,以及稀有以太猫的产出应该是什么样的规律。这些都是需要进行慎重的考虑。如此重要的部分,如果是被写死,那么风险将会极为巨大。比如,如果有人猜出了对应的生成算法,那就有可能大量生成稀有以太猫。这在传统游戏中,可能还不会致命。但是在以太猫中却比较麻烦,因为以太猫的价值实在太高,经过很多人的交易,一只稀有的以太猫可以卖出天价。对以太猫经济的保护本质上就来自于这个养成算法。
以太猫的养成的部分来自于合约 KittyBreeding
,其中可以通过 _breedWith
方法来让母亲猫怀孕。_breedWith
方法接收母亲猫和父亲猫的 id 作为参数,然后让母亲猫进入怀孕状态,合约中有另一份方法 giveBirth
来负责生出下一代以太猫。 giveBirth
中的部分代码如下,由于代码比较多,大部分进行了省略。
function giveBirth(uint256 _matronId)
external
whenNotPaused
returns(uint256)
{
......
uint256 childGenes = geneScience.mixGenes(matron.genes, sire.genes, matron.cooldownEndBlock - 1);
uint256 kittenId = _createKitty(_matronId, matron.siringWithId, parentGen + 1, childGenes, owner);
......
return kittenId;
这里可以看到,geneScience.mixGenes
方法会融合母亲猫和父亲猫的基因,然后得到下一代以太猫的基因。之后在通过基因就能够产生出新的以太猫的 id,这个 id 本质上就代表了一只全新的以太猫。
如果我们去翻看整个以太猫的合约代码,就会发现 geneScience
的类型是合约 GeneScienceInterface
,但是 GeneScienceInterface
却没有任何的实现代码。在这里以太猫使用了以太坊的一个特新,那就是指向一个外部合约。在合约 KittyBreeding
中有一个叫做 setGeneScienceAddress
的方法,代码如下
function setGeneScienceAddress(address _address) external onlyCEO {
GeneScienceInterface candidateContract = GeneScienceInterface(_address);
require(candidateContract.isGeneScience());
geneScience = candidateContract;
}
可以看到,这个方法是通过传入一个智能合约地址来生成前边提到负责生成下一代以太猫基因的 geneScience
。通过这种方式,当以太猫想要修改生成下一代以太猫的基因的算法的时候,只需要在以太坊上部署一个新的 GeneScienceInterface
合约,然后在使用 setGeneScienceAddress
指向这个新的合约地址,其中完全不需要对以太猫的主合约进行任何的修改。本质上就是实现了一种动态加载。