1

I am trying to make sure I understand something correctly, so I will pose a problem with a solution.

Problem:

Let's say commit has been called. After this, we call the reveal. attacker listens to commit transactions, and calls the commit was a front-run by himself. Then, attacker listens to reveal transactions. What attacker does now is as soon as he figures out there's a new reveal transaction in the pool, he grabs the arguments(these are the arguments how the hash was derived from), hashes them and if it matches the one already stored on his own address when he called commit , then attacker will also front-run the reveal transaction.

Solution:

I think the only solution to the above problem is that we include msg.sender while getting the commitment hash. This way, even if attacker listens to reveal transactions, and front-runs it, it won't be enough, because msg.sender of attacker will be different and it won't produce the same hash as commitment.

Question 1: What do you think ? Am I right about Solution above ?

Question 2: I've seen some implementations that msg.sender is not included while deriving a commitment hash. This means that front-run still exists, which means attacker front-runs commit , and then front-runs reveal. If there're lots of transactions happening, sure, this front-run doesn't make any sense to the attacker, but I don't think commit-reveal scheme solves front-run if we use it for quiz smart contract where user submits the solution and gets the reward, because attacker can listen to commit again and it's highly likely that whoever commits, is most likely the winner, so attacker will also wait for the reveal from the same user who called commit and front-runs again. What do you think about this ? For me, for quiz systems, commit/reveal doesn't seem a good solution to solve front-running. Could you put some examples where it would really be useful ? NOTE: I know commit/reveal is a good scheme, but I need examples where it solves the front-running problem and doesn't use msg.sender in the hash too.

2

Question 1: What do you think ? Am I right about Solution above ?

Agree. To prevent front-running, use a factor like msg.sender in the hash function so the front-runner can know the secret and still be unable to use that knowledge.

For example, consider a piggy bank with a secret word that releases the money. If the withdraw function uses (pseudo) hash(secret) then the front-runner can learn the secret word and try to claim the money first. If the function uses hash(msg.sender, secret), then front-running is prevented - the secret has to be sent from a certain address - two factors instead of one.

You should also consider how the contract executes the awarding of the prize. Consider msg.sender.transfer() versus rightPerson.transfer(). You want to be sure that the effect is that only the correct account ever actually wins.

For certain apps, you might want to make it possible for anyone to pay gas to push funds to the right person. Suppose there is no reason for Alice to want to leave her funds in place and Bob wants to be a hero and pay her gas. That can work as long as the contract is certain about where the money should go.

mapping(bytes32 => unit) public pendingPayments;

function beAHero(address receiver, bytes32 secret) public {
  bytes32 key = keccak256(abi.encodePacked(receiver, secret));
  uint amount = pendingPayments[key];
  if(amount > 0) {
    pendingPayments[key] = 0;
    receiver.transfer(amount);
    // emit ...
  }
}

Alice could give Bob her secret and all Bob would be able to do is pay for gas, if he wants to. Alice gets the money in any case. If receiver.transfer() is instead msg.sender.transfer() then anyone who knows the secret can take Alice's money, including front-runners who listen to the pending transactions.

(just a scribble, so please forgive my typos).

Hope it helps.

7
  • Great Answer, Rob. It really helps. What about my second question ? – Nika Kurashvili Feb 20 at 21:33
  • TBH, I don't really understand the second question. I thought possibly my answer would help you look at those examples and work out if they're doing it right or doing it wrong. It's great to follow tail lights in a snow storm, but don't follow other cars into the ditch. ;-) – Rob Hitchens Feb 20 at 21:42
  • Just to quickly explain the second question, imagine there's a contract about quiz and it rewards user that solved the quiz. If we use commit/reveal and don't include msg.sender in the hash, then we have the same front-run attack, because attacker front-runs commit transaciton and then reveal transaction and gets the reward, which seems very possible and wrong to me. what do you think ? – Nika Kurashvili Feb 20 at 21:45
  • if someone is not very careful, it could be still beneficial for an attacker to front-run commit and then reveal transactions of the same user. I brought quiz example. Let's say the commit just uses hash of secret + solution.Nothing else. and in the reveal, after all the check, we do this msg.sender.transfer.What happens here is that attacker front-runs commit transaction, because whoever commits transaction, it's highly likely they solved the quiz. So attacker then also listens to the reveal transaction from the same user and front-runs reveal transaction. don't you agree? – Nika Kurashvili Feb 21 at 8:43
  • Any transaction can potentially be front-run(ned) - includes the commit. Whether the honest sender would reveal when they can see their commit lost out to someone else depends on app details. One thing you should always do is prevent the re-use of a known hash - previously seen, so the victim would see that their transaction failed. If they walk away and never reveal then the attacker doesn't know how to finish. Still, things are greatly simplified by relying on the msg.sender signer in the hash function. – Rob Hitchens Feb 21 at 18:43
1

Some remarks:

  • Usually commit and reveal phases are separated such that no commit message are allowed after certain point. That way if someone send a reveal message an attacker no longer can send a commit with the answer.

  • The committed message must include something extra that an attacker cannot alter like msg.sender or something he cannot guess like a random nonce.

Commit+reveal is a technique that helps prevent some cases of front-running but it is not an efficient situation requiring two transactions and several blocks in between those transactions.

16
  • Thanks @Ismael, but I think my 2nd question in the actual question stays unanswered. Just to quickly explain the second question, imagine there's a contract about quiz and it rewards user that solved the quiz. If we use commit/reveal and don't include msg.sender in the hash(also not any kind of address), and then we have msg.sender.transfer somewhere in the reveal, then we have the same front-run attack, because attacker front-runs commit transaciton and then front-runs reveal transaction and gets the reward, which seems very possible and wrong to me. what do you think ? – Nika Kurashvili Feb 21 at 8:39
  • if someone is not very careful, it could be still beneficial for an attacker to front-run commit and then reveal transactions of the same user. I brought quiz example. Let's say the commit just uses hash of secret + solution.Nothing else. and in the reveal, after all the check, we do this msg.sender.transfer.What happens here is that attacker front-runs commit transaction, because whoever commits transaction, it's highly likely they solved the quiz. So attacker then also listens to the reveal transaction from the same user and front-runs reveal transaction. don't you agree? – Nika Kurashvili Feb 21 at 8:43
  • @NikaKurashvili It is problematic if someone can front run your commit message. – Ismael Feb 21 at 16:03
  • I just brought an example of one scenario. Don't you agree with my example that it's possible in my scenario about quiz ? – Nika Kurashvili Feb 21 at 17:05
  • @NikaKurashvili I'd try to avoid the possibility of someone front running a commit message. For example making impossible to commit the same message more than once. – Ismael Feb 21 at 20:27
0

If the malicious owner sees the hash you submitted and submits the same hash, your solution breaks down.

Commit-reveal can be used only if it is hidden when committing and open when revealing. When committing, there are so many people who have committed it, so it should be difficult to know who's right.

A suitable example of commit-reveal is to answer a quiz or bid.

5
  • Then, how does commit/reveal solve front-running attack in the first place ? because malicious owner can just listen to commit transactions and submit the same hashes. Then they can also listen to reveal transactions and submit the solutions again. – Nika Kurashvili Feb 20 at 15:35
  • Because many people commit and don't know who's the right answer, there is no motivation for the attack. You just need to make it cost more than profit. – smarteasy Feb 20 at 16:01
  • I think it depends on what we use commit/reveal for. If there's a quiz on the smart contract, and whoever submits the solution, gets some money, I don't think commit/reveal would help in this case. I can explain why. let's say we use commit/reveal for this case. if someone is submitting a solution via commit, it's highly likely that he solved the quiz. so malicious listens to commit transactions, and then listens to reveal transactions and still wins in this case. Don't you agree ? – Nika Kurashvili Feb 20 at 18:39
  • Updated my answer ! – Nika Kurashvili Feb 20 at 20:48
  • Many people think that they have taken the quiz, but few people get it right. It is not economical for an attacker to submit the same answer each time he submits a quiz answer. – smarteasy Feb 21 at 0:20

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.