The short answer to this is that validators are not punished for simply voting, but rather they are punished for intentionally voting maliciously.
Note: the post you linked is relatively old now (2015) and somewhat out of date. The information below is taken from the Casper The Friendly Finality Gadget research paper and the Minimal Slashing Conditions Article, both by Vitalik in 2017.
The determining factor for the order of blocks and their contents does not come straight from a validator himself, but rather from a proposal mechanism. This mechanism is what chooses the transactions sent with each block, and is purposely kept abstract:
[the proposal mechanism] can be a dictator, it can be a round-robin
scheme between the participants in the consensus, or, as in our case
with hybrid Casper, it will be the original proof of work chain.
Given this knowledge, the next step is to understand the point of the validators. The validators come into play every checkpoint (one hundredth block in the chain). The period between checkpoints is called an epoch. During each epoch, validators have the ability to send two classes of messages,
commit, as defined below:
[PREPARE, epoch, hash, epoch_source, hash_source]
[COMMIT, epoch, hash]
The job of the validators is to wait for the proposal mechanism to create a checkpoint during an epoch. When this occurs validators must send a
prepare message with the parameters given by the proposal mechanism. In order to help mitigate any malicious intent, the validators must then wait until 2/3 of the active validators has sent this same
prepare message. When this occurs, the next phase is triggered and the validators send the accompanying
commit message. If two thirds of validators do this, the checkpoint is considered finalized. The most important thing to realize is that validators do not simply cast one vote to determine the next block as well as their reward or their penalty.
There may be edge cases, as cited in the research paper:
Although we say that validators "should" follow the above set of
rules, in many circumstances there is no way to enforce that they are
in fact doing so. For example, consider a case where the proposal
mechanism forks and creates two competing checkpoints at epoch n, C1
and C2. Suppose that a validator sees C1 five seconds before C2.
According to the above rules, the validator should prepare on C1.
However, if the validator prepares on C2, this cannot be detected,
because for all we know the message containing C1 could have been
delayed by six seconds en route to that validator's computer and so
the validator could have seen C2 first.
This scenario would not cause a validator to lose his funds. Instead, Casper has a set of rules known as Slashing Conditions, where if any validator triggers one of the four conditions they will lose their entire deposit. The four conditions are as follows (see the end of the post for a technical description of the conditions):
- Sending a commit requires seeing 2/3 prepares
- If you make a prepare in some epoch pointing to some particular previous epoch, then you need to have seen 2/3 prepares in that epoch, and those prepares must point to the same previous epoch
- If you make a commit during some epoch, then you clearly saw 2/3 prepares during that epoch, and so any future prepares that you do should better be referencing that epoch or something newer
- You can’t prepare twice in a single epoch
Technical Description of Slashing Conditions
[COMMIT_REQ] If a validator sends a signed message of the form
["COMMIT", epoch, HASH]
then unless, for some specific value epoch_source, with -1 <= epoch_source < epoch, messages of the form
["PREPARE", epoch, HASH, epoch_source]
have been signed and broadcasted by 2/3 of validators, then that validator’s deposit is slashed.
[PREPARE_REQ] If a validator sends a signed message of the form
["PREPARE", epoch, HASH, epoch_source]
where epoch_source != -1, then unless, for some specific value epoch_source_source , with -1 <= epoch_source_source < epoch_source , messages of the form
["PREPARE", epoch_source, ANCESTOR_HASH, epoch_source_source]
, where ANCESTOR_HASH is the (epoch — epoch_source) — degree ancestor of HASH, have been signed and broadcasted by 2/3 of validators, then that validator’s deposit is slashed.
[PREPARE_COMMIT_CONSISTENCY] If a validator sends a signed message of the form
["COMMIT", epoch1, HASH1]
and a prepare of the form
["PREPARE", epoch2, HASH2, epoch_source]
where epoch_source < epoch1 < epoch2 , then, irrespective of whether or not HASH1 ?= HASH2, the validator is slashed.
[NO_DBL_PREPARE] If a validator sends a signed message of the form
["PREPARE", epoch, HASH1, epoch_source1]
and a signed message of the form
["PREPARE", epoch, HASH2, epoch_source2]
where HASH1 != HASH2 or epoch_source1 != epoch_source2, but the epoch value is the same in both messages, then the validator is slashed