Solidity石头剪刀布(RPS)合约未出招校验逻辑疑问
结论:这段合约代码确实存在你所说的逻辑冲突,属于明显的设计漏洞
- 首先明确Solidity的基础规则:
mapping(address => uint8) public choices这类映射类型,未手动赋值的键对应的默认值就是对应类型的零值,uint8的零值就是0。合约开发者原本的设计意图是用0代表「玩家未提交出招」的状态,但同时又将代表石头的常量ROCK赋值为0,二者确实会发生冲突。 - 冲突会引发的具体问题:
- 玩家首次提交
ROCK(值为0)出招后,choices[msg.sender]会被赋值为0,后续玩家仍然可以重复调用play函数修改出招——因为require(choices[msg.sender] == 0)的校验会一直通过,完全起不到「防止重复出招」的作用。 - 胜负判定逻辑会混乱:如果一个玩家未出招、另一个玩家出了
ROCK,evaluate函数读取二者的choices值都是0,会错误判定为平局。
- 玩家首次提交
- 常见的修复方案:
方案一:调整出招常量的取值,避开零值。比如将ROCK赋值为1、PAPER为2、SCISSORS为3,零值0专门用来标识未出招的状态。
方案二:新增独立的状态标记校验是否已出招,比如添加mapping(address => bool) public hasPlayed,play函数里改为校验require(hasPlayed[msg.sender] == false),出招成功后将hasPlayed[msg.sender]设为true即可,无需和出招值的取值范围耦合。
内容的提问来源于stack exchange,提问作者celtics4lyfe
相关产品推荐
相关产品推荐

