Many contracts, like game contracts, usually need to keep some values as private. For example, storing the player’s next move or a number that has to be guessed. Private variables are not directly readable by a user or a contract, but that still doesn’t guarantee secrecy.

In solidity, using private may look like a very straightforward option for storing secret values. However, this doesn’t restrict anyone from reading the state of the contract. This is because the data that is stored in the transactions made to a contract is readable.

Let’s consider an example of an odd-even multiplayer game contract, which chooses the winner based on the numbers guessed. Each player chooses a number and if the sum is even then the first player wins, otherwise the second player wins.

The game contract stores the bets of two players in players mapping. Since this variable is declared as private, the second player cannot read the data. Each player has to transfer 1 Ether to the contract in order to play. This condition is checked using require .

Once the second player places his bet, the winner is selected based on the odd-even logic and gets the whole bet amount of both players. A malicious player can carry on an attack, which always allows him to win a game. The attacker waits for someone to make a first move and plays as a second player. Now, although the variable is private, the attacker can read the first player’s bet by decoding the Ethereum transaction used. This allows the second player to choose a value that always makes him the winner.

The contract code in the above example is used only for illustration purposes and may contain bugs. It is strongly recommended to audit this code prior to any use.

How to read private state variables

State changes in Ethereum are usually done through a transaction. If the receiving account is a contract in a transaction, the EVM runs the contract’s code either to completion or until the execution runs out of gas.

Details, such as which method to call and input parameters are specified in the data field of each transaction. For example, for modifying a private state variable in the contract, you need to pass the “private” value to the setter method through a transaction. Considering the fact that every transaction data is visible to all the nodes, you could easily read private variables if you know the transaction.

Let’s look into the odd-event contract that we discussed above and see how you can decode the transaction data.

For every method call, the transaction data will have 2 fields:

Method selector Method parameters

In our smart contract, call to the method will have the following transaction data. Let’s try to decode this.

0x6587f6ec0000000000000000000000000000000000000000000000000000000000000064

First 4 bytes of the transaction data always points to the method signature. It is calculated by taking the first 4 bytes of keccak hash of the method signature. In our example, it is bytes4(keccak256('play(uint)')) which is 0x6587f6ec .

The following characters point to the parameter of the specified method. Each parameter is represented by the hex value of the input padded to 32 bytes. If 100 is the input parameter to play method, then the transaction data for that parameter is:

0x0000000000000000000000000000000000000000000000000000000000000064

This can get more complex if there are more parameters and they are dynamic. You can get more details about argument encoding from the official solidity documentation.

Recommendations for storing private information

You may have requirements to store private variables in your contract, but that’s a rather complex problem. There have been a lot of attempts to simulate a private store of information and one interesting approach is the use of a commit reveal pattern. In this method, users first submit the hash of a secret information and when everyone else has submitted theirs, each participant reveals their vote, which can be back verified.

This is not suited for all the applications, and it adds a lot of complexity on users, but it’s a starting point for further explorations.