Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finalizers can be slashed for legitimate behavior #902

Open
scravy opened this issue Apr 5, 2019 · 4 comments
Open

Finalizers can be slashed for legitimate behavior #902

scravy opened this issue Apr 5, 2019 · 4 comments
Labels
bug A problem of existing functionality consensus
Milestone

Comments

@scravy
Copy link
Member

scravy commented Apr 5, 2019

As discussed in https://github.com/dtr-org/unit-e/pull/852/files#r272530687, it looks like finalizers can be slashed for valid behavior.

#852 even produces a test that succeeds, but apparently should not.

@scravy scravy added bug A problem of existing functionality consensus labels Apr 5, 2019
@dtr-org dtr-org deleted a comment from mergeable bot Apr 5, 2019
@frolosofsky
Copy link
Member

frolosofsky commented Apr 5, 2019

There're two interconnected values in the vote transaction: target_hash and target_epoch. Both are set up in this way:

    m_recommended_target_hash = block_index.GetBlockHash();
    m_recommended_target_epoch = GetEpoch(block_index);

and then these values are propagated to the vote transaction through FinalizationState::GetRecommendedVote().

But on the "server" side we do not have a check that vote.target_epoch and vote.target_hash refers to the same block in the blockchain. We have the check which doesn't guarantee these values consistency.

  if (targetHash != m_recommended_target_hash) {
    return fail(Result::VOTE_WRONG_TARGET_HASH,
                "%s: validator=%s is voting for target=%s instead of the "
                "recommended_target=%s.\n",
                __func__, validatorAddress.GetHex(), targetHash.GetHex(),
                m_recommended_target_hash.GetHex());
  }

We need at least one more check that vote.target_epoch == m_recommended_target_epoch.

The current implementation of double vote detection is also incorrect, I guess.

    // Check for double votes
    const auto recordIt = voteMap.find(vote.m_target_epoch);
    if (recordIt != voteMap.end()) {
      if (recordIt->second.vote.m_target_hash != vote.m_target_hash) {
        return recordIt->second;
      }
    }

This effectively means that to be slashes, finalizer must send double vote with incorrect target hash. At the moment, sending double votes with correct hashes is legit.

I think, the correct behavior is:

  • detect inconsistency in target_hash and target_epoch and reject such transaction.
  • do not slash when target_hash and target_epoch differs.
  • slash when target epochs are same and target hashes are same.

@thothd
Copy link
Member

thothd commented Apr 5, 2019

@frolosofsky , to make it clear when looking at the history of #852 and this issue and make sure we converge to clear rules when to slash, we can't see it in this issue since it only refers to a comment and the test code in the PR changed but the discussion @scravy pointed at was about slashing when the target epochs are different, as was also described in the other PR's test.
The suspicion was that we slash for the wrong reasons, then @kostyantyn mentioned that it was actually a slashable condition since it's a surround vote.

You are describing another issue related to the target hash. I wouldn't say it's a definite case of wrong implementation of the slashing mechanism (as with target epoch @scravy mentioned). A suggestion not to slash when the target hash and epoch are different. I understand the idea why we probably shouldn't slash in this case since it looks more like an invalid vote than something that can be processed other than capital punishment. But this case has more room for interpretation.

@thothd thothd added this to the 1.0 milestone Apr 5, 2019
@kostyantyn
Copy link
Member

kostyantyn commented Apr 5, 2019

The reason we have target_epoch besides target_hash is that we don't want to download the entire fork to figure out if we want to slash or not, so we rely on target_epoch in this case.

I think it's fine to slash based on the target_source/target_epoch as this is what finalizer tells you and signs it with his key. If we want to guarantee consistently, it makes more sense then to drop target_epoch and download all the headers to rely on target_hash only. But it increases the data transfer usage.

@frolosofsky
Copy link
Member

Ok I realized why do we have target_hash, and the intention of why vote recorder is implemented in this way: we slash double votes on different branches and ignore them on the same branch — we use target hash to distinguish forks! Yeah, I think it's absolutely correct.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A problem of existing functionality consensus
Projects
None yet
Development

No branches or pull requests

4 participants