智能合约solidity
假设您正在编写Solidity智能合约,并且其属性之一可以描述为类型或状态 。 换句话说,从有限的一组选项中得到的东西。 您立即对自己说: “好极了,我只将枚举类型用于此状态变量。” 一方面,这种方法具有一些好处,例如增加了可读性。 另一方面,它可以轻松地带您走上一条可能导致问题的棘手的道路。
你在说什么?
好吧,如果枚举成员仅封装在一个合同中,而在其他文件中从未提及,则一切正常。 但是,DApp通常由相互关联的多个合同组成。 当相同的枚举出现在我正在谈论的问题:
- 出现在多个合同中, 并且
- 在DApp生命周期中进行了修改。
例如,您有2个合同。 第一种是用于存储非常重要信息的存储 。 您还声明了带有枚举定义的接口以引用该接口。
contract IStorage { enum RecordState {StateA, StateB}function setState ( address user, RecordState newState ) public ;
}contract Storage is IStorage { mapping( address => RecordState) public states;constructor () public {}function setState ( address user, RecordState newState ) public {states[user] = newState; }
}
每个用户的记录都由两个可能的选项的枚举表示:StateA和StateB。 setState函数能够更改用户的状态。 最终用户还应该与之交互另一个合同(为简单起见,我在存储合同中省略了访问控制修饰符)。
contract StorageUser {IStorage public recordStorage;constructor (IStorage _recordStorage) public { recordStorage = _recordStorage;}function changeStateA () public { recordStorage.setState(msg.sender, IStorage.RecordState.StateA); }function changeStateB () public {recordStorage.setState(msg.sender, IStorage.RecordState.StateB); }
}
然后将这些合同部署到区块链。
一切都好:
您调用changeStateA或changeStateB,并且存储合同的数据将通过其自己的setState函数进行相应的修改。 但是有一天,您意识到对于某些全新功能,您需要一个全新的状态选项。 您称它为StateC(哇!真叫!)。 首先,通过在IStorage中添加新的枚举成员来修改源代码…
enum RecordState {StateA, StateB, StateC}
…以及StorageUser的新方法。
function changeStateC () public { recordStorage.setState(msg.sender, IStorage.RecordState.StateC);
}
此外,作为负责任的开发人员,您编写调用新方法的测试,并且它们报告成功。
您的计划是仅重新部署StorageUser合同,并且您不想重新部署Storage ,因为映射形式有很多重要数据,这些数据很难迁移。 因此,使用当前的Storage作为其构造函数参数重新部署StorageUser 。 您调用新的changeStateC函数...,它将失败。
等等,什么? 为什么? 测试还可以。
故障原因
您会看到,更新后的StorageUser知道RecordState枚举的3个成员,但是旧的Storage没有关于新StateC选项的线索。 它无法将setState函数参数StateC转换为其枚举版本,因此失败。
而且,您的测试可能欺骗了您,因为它们使用了两个合同的更新版本。
实际上,您甚至可以在官方文档中阅读有关此问题的警告。
从整数进行显式转换会在运行时检查该值是否位于枚举范围内,否则会导致失败的断言。
可悲的事实是,有时您只是忽略了这些事情,自己碰到了这些问题。
经验教训
首先,在上述情况下,最好用普通整数替换枚举。 是的,它们看起来不太好,但是生成的结构更可靠且可扩展。
其次,不要抛弃使用枚举字段的整个想法。 如果这样的领域只在一个合同之内,那绝对是安全的。 如果可以确保在进行修改的情况下完全重新部署使用枚举的所有合同,这也是安全的。 请记住,当将枚举首先从IStorage导入到StorageUser合同时,出现了问题,并且在修改初始成员后仅重新部署了后者。
只是不要忘记,如果您真的想在合同中使用枚举 ,最好三思而后行。
尝试一下
该示例的源代码已上传到GitHub 。 我已经在测试文件中重现了有问题的行为。 随时检查它并进行实验。
学分
- 人的图象在森林里。 Pexels的Dewa Prabawa 摄
- 小猫的图象。 Pexels的Vadim B 摄影
翻译自: https://hackernoon.com/beware-the-solidity-enums-9v1qa31b2
智能合约solidity