作者:Monaj
翻译:西溪明月
编辑:格鲁特
以太坊因缔造“区块链2.0”时代而出道
以方便应用创建,其社区庞大驰名全球
然而
……
技术进步的道路漫漫
……
漫漫
……
所以
……
各种各样的Bug
肯定很多嘛
以太猫这种耳朵长茧的梗就不提了
今天再给大家介绍一款Bug!
程序员都知道 建议回避
(或者你可以看完,说不定有纠错的机会)
本文举了一个基于游戏里智能合约的例子
所以主要场景也是围绕此讨论
Step1:直接进入正文
许多合约,比如游戏合约,通常需要保密一些数值。比如,存储玩家的下一步行动或等着被猜的数字。虽然用户或合约都不能直接读取私有变量,但这仍难以保证一定能够保密。在Solidity语言中,使用private似乎是存储秘密值的一个非常简单的方法。然而,这并不会限制任何人读取合约的状态。这是因为合约交易中存储的数据具有可读性。
我们以多人奇-偶游戏合约为例,该合约基于被猜的数字来选出获胜者。每个玩家选择一个数字,如果所选数字之和为偶数,那么第一个玩家获胜,若为奇数,则第二个玩家获胜。
// WARNING: This is a sample contract to demonstrate the functionality. Don't use it in your project.
pragma solidity ^0.4.21;
contract OddEven {
struct Player {
address addr;
uint number;
}
Player[2] private players;
uint8 count = 0;
function play(uint number) payable public {
require(msg.value == 1 ether);
players[count] = Player(msg.sender, number);
count++;
if (count == 2) selectWinner();
}
function selectWinner() private {
uint n = players[0].number + players[1].number;
players[n%2].addr.transfer(address(this).balance);
delete players;
count = 0;
}
}
游戏合约将两个玩家的赌注(即选出的数字)存储在players映射中。由于这个变量是不公开的,所以第二个玩家不能读取到这个数据。而每个玩家必须向合约转1个以太币游戏才能开始。这个条件要用require来核查。
一旦第二个玩家下好赌注(即选好数字),按照奇-偶逻辑会选出获胜者,他将得到两个玩家下的所有赌注金额。如果心怀不轨的玩家实施攻击,他就可以一直赢。攻击者会等对方先走第一步,自己扮演第二个玩家。现在,尽管变量是隐私的,但通过解码所使用的以太坊交易攻击者能够读取第一个玩家所选的数字。这样他就可以选择让自己赢的那些数值。
上面这个例子中的合约代码仅用于说明,可能存在漏洞。强烈建议使用前进行审核。
如何读取私有状态变量
How to read private state variables
以太坊上的状态变更通常通过交易来实现。如果接收账户是交易的合约,那么EVM(以太坊虚拟机)将运行合约代码,或直到代码运行完毕或直到执行过程耗尽所有gas。
每项交易的数据字段规定了细节(如调用哪种方法和输入参数等)。比如,在合约中修改私有状态的变量,你需要通过交易将“private”数值传递给setter方法。考虑到每个交易数据都对所有节点可见,所以如果知道一项交易,就可以轻易读取其中的私有变量。
我们回顾一下上文讨论的奇-偶合约,看看应如何解码交易数据。对于每一种方法调用,交易数据都将有2个字段:
1、Method selector 方法选择器
2、Method parameters 方法参数
在我们的智能合约中,调用方法会输出以下交易数据。我们来尝试对此解码。
0x6587f6ec0000000000000000000000000000000000000000000000000000000000000064
交易数据的前4个字节指向方法签名。它是通过计算方法签名的keccak散列的前4个字节得出的。在我们的例子中,是通过计算bytes4(keccak256('play(uint)'))函数得出的0x6587f6ec。以下字符指向指定方法的参数。每个参数由填充至32字节的输入的十六进制值表示。如果100是play方法的输入参数,那么该参数的交易数据就是:
0x0000000000000000000000000000000000000000000000000000000000000064
如果有更多参数且这些参数是动态的,情况就会变得更加复杂。你可以从solidity文件中获得更多信息。
存储私有信息的一些建议
Recommendations for storing private information
在合约中,你可能会有存储私有变量的需求,但这个问题会很复杂。对于存储私有信息,人们已经进行了大量的模拟尝试,其中有一个方法很有趣,它采用的是提交显示模式(commit reveal pattern)。在这个方法中,用户需要首先提交私有信息的哈希,当所有其他人也提交了他们的私有信息的哈希时,每个参与者再亮出自己的投票,之后可以对此进行验证。
这并不适合所有的应用程序,并使用户的操作变得更加复杂了,但抛砖引玉,它为未来的探索打开了一扇门。
作者简介:
Manoj 开发人员、顾问、作者,以太坊爱好者。
零识仅为翻译中文供大家学习使用,本文版权归英文原作者所有。
公众号:零识区块链(ID:zkchainnews)
领取专属 10元无门槛券
私享最新 技术干货