0

I'm developing a Rock/Paper/Scissors Smart Contract (with a Commit-Reveal scheme).

I have this enum: enum Move {rock, paper, scissors}

I want to pass it to a function, without storing it to the blockchain.

I tried this:

// Contract deployer is Player 1
constructor(Move memory move) payable {
    require(msg.value > 0, "Player 1 must bet a positive amount.");
    bytes32 hashedMove = keccak256(abi.encodePacked(move));
    emit MoveCommit(msg.sender, hashedMove);
}

But constructor(Move memory move) throws "data location can only be specified for array, struct or mapping types".

So I have to do this:

// Contract deployer is Player 1
constructor(Move move) payable {
    require(msg.value > 0, "Player 1 must bet a positive amount.");
    bytes32 hashedMove = keccak256(abi.encodePacked(move));
    emit MoveCommit(msg.sender, hashedMove);
}

Will this store the chosen move in the Blockchain and ruin my Commit-Reveal scheme?

1

When you declare an enum, each element is assigned an integer sequentially. In your case:

rock = 0
paper = 1
scissors = 2

Not sure why you want to pass this enum in the constructor, but if you want to implement a commit-reveal scheme, you should have at least commit() and reveal() functions where you pass the player's move, and you probably need also another enum to manage the game state (i.e: created, committed, revealed).

In order to keep the move secret during the commit phase, you could do something like this:

function commitMove(
    uint256 moveId,
    uint256 salt
) external {
    //...
    require(
        moveId == 1 || moveId == 2 || moveId == 3,
        "move must be either 1, 2 or 3"
    );
    moves[msg.sender] = keccak256(abi.encodePacked(moveId, salt));
    //...
}

So you basically store the hash of your move with a salt. The salt is the random input that each player will use when committing the move, so that during the reveal phase, each player will use to unveil its move.

Note that the moveId parameter you pass in the function is a uint256, because each value from an enum equals an integer (as described at the very beginning). Then during your code, you can always use statements such as:

if (Move.rock == moveId) //...
4
  • I want to pass it in the constructor because I want to assign the contract deployer as Player 1 (is this a good idea?). Also, in your example, wouldn't moveId be stored/visible in the blockchain? – António Gonçalves Feb 20 at 17:51
  • All players should be able to call the commit & reveal functions, so there is no difference on who you assign the contract deployer to. Only the hashed value will be stored in the blockchain, so data explorers won't be able to deduce anything from that data. For additional security, perhaps you could send the hashed value already from your front-end to prevent potential front-running/miner manipulation. – Sergi Juanati Feb 20 at 18:01
  • Are you sure moveId isn't visible in the Blockchain? I just had somebody tell me otherwise... – António Gonçalves Feb 20 at 22:12
  • As mentioned, the most secure approach is to send the hash with the moveId+salt already as data input. Indeed, if you send the moveId as data input, even only the hash will be stored ultimately, with etherscan.io you can see the values you send as data input (just decoding from hex to ascii). See 'input data' in this random example: etherscan.io/tx/… – Sergi Juanati Feb 21 at 6:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.