Smart Contract Security Class (No.005)

TRON DAO
3 min readJun 13, 2019

--

In this class, we will talk about the big issue about struct in Solidity, and the possible vulnerabilities. At the same time, everyone is welcome to follow @tron official twitter, and submit your contract code through TRON-Eye.

The following is the contract code from https://troneye.com (hereinafter referred to as TRON-Eye). TRON-Eye is a TRON verification platform from the community. The previous classes have introduced the TRON-Eye verification platform in detail. This time involves 3 contracts, RouletteV0 (contract address: TPJyhLPZqbxZqhU5XdCKTRLwM2BdeUHtqn), RouletteV1 (contract address: TJaDCgk9FV1ycDHRbZeH45RcnCxovp7tmj), RouletteV2 (contract address: TS5qqRT6D8k9uYnChVFZGwYtuVvXGMMNNQ).

Figure 1 contract RouletteV0on TRON-Eye

The code for the RouletteV0 contract is very short, the core code is as follows.

1. contract RouletteV0 {

2. uint256 public secretNumber;

3. uint256 public lastPlayed;

4. uint256 public betPrice = 1000 trx;

5. struct Game {

6. address player;

7. uint256 number;

8. }

9. Game[] public gamesPlayed;

10. constructor() public {

11. shuffle();

12. }

13. function shuffle() internal {

14. // randomly set secretNumber with a value between 1 and 20

15. secretNumber = uint8(keccak256(abi.encodePacked(now, blockhash(block.number-1)))) % 20 + 1;

16. }

17. function play(uint256 number) payable public {

18. require(msg.value >= betPrice && number <= 10);

19. Game game;

20. game.player = msg.sender;

21. game.number = number;

22. gamesPlayed.push(game);

23. if (number == secretNumber) {

24. // win!

25. msg.sender.transfer(address(this).balance);

26. }

27. shuffle();

28. lastPlayed = now;

29. }

The name of the contract is called roulette. When the contract is initialized, a random number of 1–20 is set: secretNumber. The player tries to guess the number by calling play(). If it is correct, the player can take away all the money in the contract and reset the random number secretNumber. For the convenience of presentation, the contract’s secretNumber is set to public.

Play() needs to meet two conditions to get the contract money:

1. msg.value >= betPrice, which means that at least 1000 trx needs to be sent for each quiz.

2. number <= 10, the number of the quiz cannot be greater than 10.

The contract looks like you get 50% chance of taking all trx or throwing a bet. So is that true? A few simple tests will reveal that it you will never win. Why?

19. Game game;

20. game.player = msg.sender;

21. game.number = number;

22. gamesPlayed.push(game);

In fact, the problem lies in the four lines of code shown above. Lines 19~21 will directly change the solt-0 and solt-1 member variables of the contract, that means, the values of secretNumber and lastPlayed will be changed to uint256 (msg.sender). ) and uint256(number). And uint256 (msg.sender) is usually a very big number, of course, play() can’t get money.

23. if (number == secretNumber) {

24. // win!

25. msg.sender.transfer(address(this).balance);

26. }

27. shuffle();

Even more amazing is that the 27 line of play() resets the secretNumber to a value in the range of 0–20. Everything looks perfect.

In fact, this is a bug in Solidity 0.4.x. The strut variable in a function without the memory or storage attribute uses the same storage as the contract member variable. When the struct is manipulated, the value of the member variable is modified. This bug has been fixed in Solidity 0.5.x.

Then the correct code should be specify the game variable to memory, so that even if it assigns a value to the game again, it will not overwrite the member variables of the slot-0 and slot-1 positions. (Complete code, see RouletteV1 at https://troneye.com/reveal?address=TJaDCgk9FV1ycDHRbZeH45RcnCxovp7tmj)

Game memory game = Game(msg.sender, number);

game.player = msg.sender;

game.number = number;

gamesPlayed.push(game);

The correct final version is simplified (see RouletteV2 at https://troneye.com/reveal?address=TS5qqRT6D8k9uYnChVFZGwYtuVvXGMMNNQ for complete code):

gamesPlayed.push(Game(msg.sender, number));

This class will be end here. Thanks to TRON-Eye for providing the contract verification tool. More information about them can be found directly on their website https://troneye.com.

We continue to call for source code for follow-up classes, and we welcome your official twitter submissions.

--

--

TRON DAO
TRON DAO

Written by TRON DAO

The official Medium of TRON DAO.

No responses yet