智能合约分析 -- 以太猫之动态修改合约行为

区块链有一个不得不说的特性就是不可篡改。基本意思是只要一段数据被写到了区块链上,那要想修改这段数据的代价会非常巨大,大到不可接受的地步其实也就等于无法修改。以太坊的智能合约虽然是代码,但代码本质上也是一段文本数据。所以当智能合约被部署到区块链上,也就不再能够修改。这是区块链的一个优秀的特性,在这种情况,当一个严肃的合约被上链之后,也就天生具备了可信任的特点。不过这个不可篡改可能和很多人想象的不一样。这里说的不可篡改是说存储到区块链上的数据不会变化,但作为智能合约来说,这种数据的不可变化并不代表合约的行为不可变化。

不可改变其实和软件开发是相悖的,要知道软件开发很难避免修改,只要是软件就有可能需要修改 bug,也可能需要添加了新的功能等等。作为 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 方法指向这个新的合约地址,这时候新的以太猫的生成也就指向了新的合约地址,整个生成行为也得到更新,这里将完全不需要对以太猫的主合约进行任何的修改,也就避免了销毁合约再升级这种破坏性极强的方法。

这种方式本质上就是实现了一种动态加载。能够在合约已经部署之后再动态修改合约的行为。所以当我们说区块链不可篡改的时候,所说的只是存储在上边的数据或者代码本身无法篡改,但是具体到代码的行为,却是完全可以进行修改的。