智能合约升级原理03---合约升级的注意事项

资料来源:【翻译】编写可升级的智能合约【翻译】编写可升级的智能合约 - 腾讯云开发者社区-腾讯云当使用OpenZeppelin Upgrades编写可升级合约时,有一些在编写Solidity代码时需要记住一些注意事项。icon-default.png?t=M85Bhttps://cloud.tencent.com/developer/article/1773818潜在的不安全操作

在使用可升级的智能合约时,你将始终与(代理)合约实例进行交互,而不是底层逻辑合约。然而我们却无法阻止恶意行为者直接向逻辑合约发送交易。这不会构成威胁,因为逻辑合约状态的任何变化都不会影响你的(代理)合约实例,因为你的项目中从未使用过逻辑合约的存储。

然而,有一个例外。如果对逻辑合约的直接调用触发了自毁操作selfdestruct,那么逻辑合约就会被销毁,你的所有合约实例最终都会将所有的调用委托给一个地址,而不会有任何代码。这会破坏你项目中的所有合约实例。

如果逻辑合约中包含委托调用delegatecall操作,也可以达到类似的效果。如果可以将delegatecall变成一个包含自毁的恶意合约,那么调用合约将被破坏。

因此,在你的合约中不允许使用selfdestruct或delegatecall。

修改你的合约

在编写新版本的合约时,无论是由于新功能还是bug修复,都有一个额外的限制需要遵守:你不能改变合约状态变量的声明顺序,也不能改变它们的类型。你可以通过了解 Proxies来阅读更多关于这个限制背后的原因。

警告 违反这些存储布局限制中的任何一项,都会导致升级版的合约的存储值被混淆,并可能导致你的应用程序出现关键错误。这意味着,如果初始合约看起来像这样:

contract MyContract {

    uint256 private x;

    string private y;
}

那么不可以修改合约变量类型:

contract MyContract {

        string private x;

        string private y;

}

也无法改变变量的声明顺序:

contract MyContract {

        string private y;

        uint256 private x;

}

不能在现有变量之前引入新的变量:

contract MyContract {

    bytes private a;

    uint256 private x;

    string private y;
}

也不能删除现有变量:

contract MyContract {

    string private y;
}

如果需要引入新的变量,请确保添加到原有变量的后面:

contract MyContract {

    uint256 private x;

    string private y;

    bytes private z;
}

注意,如果重命名一个变量,那么在升级后,它将保持与之前相同的值。如果新变量和旧变量的语义相同,那么这可能是我们所希望的行为:

contract MyContract {

    uint256 private x;

    string private z; // starts with the value from `y`
}

而如果你在合约的最后删除了一个变量,请注意存储不会被清除。随后的更新中如果增加一个新的变量,会导致该变量从被删除的变量中读取遗留的值:

contract MyContract {

    uint256 private x;
}

升级到:

contract MyContract {

    uint256 private x;

    string private z; // starts with the value from `y`
}

注意,你也可能会因为改变合约的父合约而无意中改变合约的存储变量。例如,如果你有以下合约:

contract A {

    uint256 a;
}

contract B {

    uint256 b;
}

contract MyContract is A, B {}

然后通过调换基础合约的声明顺序或引入新的基础合约来修改MyContract,将改变变量的实际存储方式:

contract MyContract is B, A {}

如果集成合约有任何自己的变量,你也不能在基础合约中添加新的变量。鉴于以下情况:

contract Base {

    uint256 base1;
}

contract Child is Base {

    uint256 child;
}

如果修改Base,增加一个额外的变量:

contract Base {

    uint256 base1;

    uint256 base2;
}

然后,变量base2将被分配到上一个版本中那个child的槽位。一个变通的办法是在基础合约上声明未使用的变量,你可能会在未来想要扩展,作为 "保留 "这些槽位的一种手段。请注意,这个技巧不会增加gas使用量。

猜你喜欢

转载自blog.csdn.net/u012084827/article/details/127483944
今日推荐