Search code examples
cryptographyethereumsoliditysmartcontracts

Hiding partial results in a smart contract


I have the following use case. There's a smart contract based game where everyone can award 1 point to a color. At the end of the game, the smart contract reveals which color is the most popular/has gained the most points. Take the following Solidity code:

struct Color{
        string colorName;
        uint256 awardedPoints; 
        uint256 colorID;
    }

mapping(uint256 => Color) private colorsMapping;

function awardPoint(uint256 colorID) public {
        colorsMapping[colorID].awardedPoints++;    
    }

Of course, the mapping is marked with the "private" keyword, but that doesn't really stop determined people from revealing the content. Besides, everyone can count the calls for function awardPoint(colorID) and deduce partial results.

What would be the best way of solving this with minimal changes to the code?

What I thought about:

  1. Every time someone calls function awardPoint(colorID), colorIDs are randomly shuffled. However, they are tied to the mapping already, so I don't really see a wait of implemnting this in Solidity. Besides, it wouldn't really solve the issue of someone revealing the data of the colorsMapping private variable.
  2. Adding random numbers to awardedPoints count every time someone calls awardpoint function, similar to weights. However, I have a hard time thinking about how to substract them later, how and where to stored them etc.
  3. Homomorphic encryption: I'm not very familiar with the concept, but I know that you can perform operations on encrypted data (in this case incrementations) and you can decrypt the results at the end. However, I have no idea how to implement this in Solidity (or at all).

What approach would be the best?


Solution

  • My suggestion is to have everyone submit the hash of [colorid, secret]. When everyone has submitted answers, everyone reveals their chosen colorid and secret.

    function submitHash(bytes hash) public {
        hashes[msg.sender] = hash;
    }
    
    function submitAnswer(uint256 colorid, uint256 secret) public {
        require(votesIn, "votes are not all in");
        require(keccak256(abi.encodePacked(colorId, secret)) == hashes[msg.sender], "invalid response");
        votes[colorId] += 1;
    }