If relays have the Exit flag, don't give them the Guard flag
Motivated by this tor-relays@ thread: https://lists.torproject.org/pipermail/tor-relays/2020-October/019014.html In short, there's an edge case in our "Bandwidth-weight Case 3a (E scarce)" where exit capacity is scarce but not that scarce, so Wgd ("the weight for choosing a Guard+Exit for the guard position") can become non-zero by a little bit.
What this means in practice is that when we hit that edge case, relays that have both the Guard flag and the Exit flag will be used just a tiny bit as guards by clients. But because of how clients actually use guards (all or nothing, rather than spreading out the load over all the guards), the reality for a relay with a tiny guard weight is that it will accumulate only a handful of clients.
And that situation is extra-scary for those few unlucky clients -- their "guard fingerprint" is a much more distinguishable feature for them because so few other people have it, and also when a guard has one-ish active user, middle relays can know that circuits coming from that guard are likely to be that user.
All in all, it seems much safer to eliminate this situation: dedicate Exits to being exits (or they can be middles too if there's enough exit capacity), and dedicate Guards to being entries (or they can be middles too if there's enough guard capacity).
I hear that @mikeperry wants this feature too, for padding efficiency, because having low-use guards but still needing to pad at them is a suboptimal use of our network bandwidth.
Now, how exactly we should implement this feature remains to be chosen. I think all of the good ways involve doing it at the dir auths (i.e. clients and relays shouldn't have to care how we do it or even that we did it). One option (super easy to do) is that if we're giving it the Exit flag, we withhold the Guard flag. Another option (requires a new consensus method, and also a Mike) is that we redo the consensus weighting to handle case 3a ("exit scarce") differently. Are there other approaches that would accomplish the goal?
There are some edge cases to consider here, for example, in a test network that is all Exits, if we therefore never give out the Guard flag, things might get bad. We could handle that either by having this setting be a torrc option that gets overwritten when TestingTorNetwork, or ...maybe Mike's plan of rewriting the linear equations will handle that edge case automatically?