36.SWC-136_Get Private Info
2023-07-13 15:58:38 # 09.SWC

SWC-136_Get Private Info

SWC-136

Everyone can see player’s number in this contract: Player.number

we can wait the first person to set number, and then we choose an appropriate number and set the number to get money .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
pragma solidity ^0.5.0;

contract OddEven {
struct Player {
address addr;
uint number;
}

Player[2] private players;
uint count = 0;

function play(uint number) public payable {
require(msg.value == 1 ether, 'msg.value must be 1 eth');
players[count] = Player(msg.sender, number);
count++;
if (count == 2) selectWinner();
}

function selectWinner() private {
uint n = players[0].number + players[1].number;
(bool success, ) = players[n%2].addr.call.value(address(this).balance)("");
require(success, 'transfer failed');
delete players;
count = 0;
}
}

Remediation:

  1. Player chooses a number and blindingFactor, then calculate keccak256(abi.encodePacked(msg.sender, number, blindingFactor). It results the commitment, while no one know your blindingFactor and number
  2. play(): locking 2 players.
  3. reveal(): you can only set the number you chose before, or you can not pass the require(). It prevents from players peep at others’ number because player encrypted his number by asymmetric encryption and he can not change this number for the require().

So if you want to hide something on the chain, maybe you can try asymmetric encryption and lock it by require()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
pragma solidity ^0.5.0;

contract OddEven {
enum Stage {
FirstCommit,
SecondCommit,
FirstReveal,
SecondReveal,
Distribution
}

struct Player {
address addr;
bytes32 commitment;
uint number;
}

Player[2] private players;
Stage public stage = Stage.FirstCommit;

function play(bytes32 commitment) public payable {
// Only run during commit stages
uint playerIndex;
if(stage == Stage.FirstCommit) playerIndex = 0;
else if(stage == Stage.SecondCommit) playerIndex = 1;
else revert("only two players allowed");

// Require proper amount deposited
// 1 ETH as a bet + 1 ETH as a bond
require(msg.value == 2 ether, 'msg.value must be 2 eth');

// Store the commitment
players[playerIndex] = Player(msg.sender, commitment, 0);

// Move to next stage
if(stage == Stage.FirstCommit) stage = Stage.SecondCommit;
else stage = Stage.FirstReveal;
}

function reveal(uint number, bytes32 blindingFactor) public {
// Only run during reveal stages
require(stage == Stage.FirstReveal || stage == Stage.SecondReveal, "wrong stage");

// Find the player index
uint playerIndex;
if(players[0].addr == msg.sender) playerIndex = 0;
else if(players[1].addr == msg.sender) playerIndex = 1;
else revert("unknown player");

// Check the hash to prove the player's honesty
require(keccak256(abi.encodePacked(msg.sender, number, blindingFactor)) == players[playerIndex].commitment, "invalid hash");

// Update player number if correct
players[playerIndex].number = number;

// Move to next stage
if(stage == Stage.FirstReveal) stage = Stage.SecondReveal;
else stage = Stage.Distribution;
}

function distribute() public {
// Only run during distribution stage
require(stage == Stage.Distribution, "wrong stage");

// Find winner
uint n = players[0].number + players[1].number;

// Payout winners winnings and bond
players[n%2].addr.call.value(3 ether)("");

// Payback losers bond
players[(n+1)%2].addr.call.value(1 ether)("");

// Reset the state
delete players;
stage = Stage.FirstCommit;
}
}
Prev
2023-07-13 15:58:38 # 09.SWC
Next