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

Miscellaneous beacon chain changes #128

Closed
18 tasks done
JustinDrake opened this issue Nov 14, 2018 · 13 comments
Closed
18 tasks done

Miscellaneous beacon chain changes #128

JustinDrake opened this issue Nov 14, 2018 · 13 comments
Labels
general:enhancement New feature or request

Comments

@JustinDrake
Copy link
Collaborator

JustinDrake commented Nov 14, 2018

Below is a summary of suggestions from miscellaneous internal discussions:

  • 1. Fair staking: Every validator has the same amount BALANCE_AT_RISK = 24 ETH at risk (lower than DEPOSIT_SIZE = 32 ETH). In particular, penalties only apply to the balance at risk (regardless of the size of the balance), and validators with a balance below BALANCE_AT_RISK are automatically exited.
  • 2. Delayed signature inclusion: Aggregate signatures for slot n can be included onchain no earlier than slot n + SIGNATURE_INCLUSION_DELAY (e.g. SIGNATURE_INCLUSION_DELAY = 4). This allows for the safe reduction of SLOT_DURATION (e.g. to 4 seconds), and reduced beacon chain overhead from more efficient aggregation. The fork choice rule is unchanged (it takes into account offchain signatures).
  • 3. Type homogenisation: All integer types are homogenised to uint64. Exceptions are made for signature types, and possibly where a significant performance penalty would be observed.
  • 4. Message domain isolation: All signed messages (including proofs of possession) contain a fork_version field which is checked as part of the same unified signature verification logic.
  • 5. Special object count limit by kind: Every kind of special (LOGOUT, CASPER_SLASHING, etc.) has a separate object count limit per block.
  • 6. Weakened no-surround slashing condition: The no-surround slashing condition is replaced by the following: A validator must not cast two votes such that target1 = source1 + 1, source2 <= source1 and target2 >= target1. (See this ethresear.ch post for context.)
  • 7. Order special objects by kind: Limits the possibility for edge cases when processing special objects.
  • 8. BLS12-381: Finalise the move to the new curve.
  • 9. Fixed-sized shard blocks: For simplicity. When the pool of validators is critically low proofs of custody can be disabled and notaries can rely on data availability proofs only.
  • 10. Withdrawal credentials: Replace withdrawal_address and withdrawal_shard with withdrawal_credentials which is composed of a version number and (for version 0) the hash of a BLS pubkey.
  • 11. PoW receipt root votes: Replace candidate_pow_receipt_root with a map from candidate PoW receipt roots to vote counts to avoid bad PoW receipt root candidates from polluting a full voting period.
  • 12. Reduce PoW receipt root threshold: From 2/3 (needlessly conservative given a committee size of 1,024) to 1/2 for improved liveness.
  • 13. Crosslink hash: Fix crosslink hashes to bytes([0] * 32) until phase 1 for cleanliness and unambiguity.
  • 14. Minimum registration period: Force validators to be registered for some amount of time (e.g. 1 week) to mitigate join-leave attacks (for when re-registrations are possible).
  • 15. Use uint64 for shard number: uint16 will plausibly be too small for the future, and is not consistent with the homogenisation to uint64. For extra hashing performance hash(n) can be cached for n < SHARD_COUNT.
  • 16. Constrain genesis time: GENESIS_TIME should fall at 00:00 UTC.
  • 17. RANDAO cleanup: Replace randao_last_change with a counter called missed_slots_streak to remove the notion of "layer" (and the edge case of a validator revealing twice in a RANDAO layer).
  • 18. Attestations per block: Set a maximum number of attestations per block.
@vbuterin
Copy link
Contributor

Now that I think about it, there is an unintended consequence of BALANCE_AT_RISK being high. Namely, during a quadratic leak, you get validators getting kicked off very quickly if their balance starts to drop below 24, so the winner of a war between two minority forks would depend not on who has more stake, but on who had more topups on their deposits. This feels to me like the wrong direction to go.

@vbuterin
Copy link
Contributor

On the other issues:

  1. Separated out proposer from attesters #143
  2. Currently, the uses of non-64-bit ints I can find are: (i) uint16 for shard IDs, which exist in many places including where they are highly repeated, though the cost of adding the 6 bytes to bring them up to 64 would be small, at most a couple of percent, (ii) uint24s for aggregate sig indices, which appear en masse and would lead to large size penalties for converting them to uint64s, (iii) uint32s in a few random areas, which I removed just now. I can see a rationale for consistently keeping all shard IDs at 2 bytes and validator indices at 4 bytes and saying that's the only exception to the uint64 rule (aside from cryptography). This would also serve a secondary function as a ghetto "type system" so could reduce bugs :)
  3. The one issue that I see is that a message could be created and includable before a fork happens, but included after a fork. So messages would need to all come with some notion of what slot number they are "for". Right now, we have deposits, logouts, beacon attestations, beacon proposals, shard proposals and shard attestations (there's also proof of custody seed changes and challenge responses but those are self-verifying so don't need signatures). I suppose the simplest abstraction would be for BLSVerify to include a "domain" field, and then we can for each message say what the domain is based on the info about the slot we can get from the message. Does that sound good?
  4. What was the rationale for this again? I know we wanted a global count limit (which we now have), but why limit by kind? Remember that there are in many case natural limits by kind, eg. one logout or slashing per login, one response per challenge, one seed change per validator per N days...
  5. Done as part of Add proofs of custody #145 (though in a different form; if you disagree with it feel free to comment)

@JustinDrake
Copy link
Collaborator Author

JustinDrake commented Nov 19, 2018

the winner of a war between two minority forks would depend not on who has more stake, but on who had more topups on their deposits

My suggestion is for the "deposit war" to be on equal terms at 24 ETH on all forks. That is, during a quadratic leak period we do not automatically exit a validator if his balance goes below 24 ETH. Instead, the automatic exit happens when 24 ETH in total leakage has accrued. When the validator comes back online his first transaction may have to be a top-up bringing his balance back to 24 ETH if he wants to remain a validator.

This would also serve a secondary function as a ghetto "type system" so could reduce bugs :)

What do you mean?

a message could be created and includable before a fork happens, but included after a fork

Can we have a "messaging clean slate" after a fork, i.e. all pre-fork offchain messages are effectively invalidated and have to be rebroadcast?

What was the rationale for this again? I know we wanted a global count limit (which we now have), but why limit by kind?

I'd argue we do not want a global count limit:

  1. It can be abused for DoS by filling blocks with maximally-expensive special objects. Per kind limits allows for smaller limits for expensive special objects.
  2. To mitigate the above issue the global count limit would have to be kept low, which introduces an inefficiency for bursts of cheap special objects that can safely be cleared in batches larger than the global count limit.
  3. In the context of a max block limit (which I would now argue against) it does not provide "guaranteed subsidised space" for large special objects which otherwise may be out-competed by smaller special objects.

@vbuterin
Copy link
Contributor

My suggestion is for the "deposit war" to be on equal terms at 24 ETH on all forks. That is, during a quadratic leak period we do not automatically exit a validator if his balance goes below 24 ETH. Instead, the automatic exit happens when 24 ETH in total leakage has accrued. When the validator comes back online his first transaction may have to be a top-up bringing his balance back to 24 ETH if he wants to remain a validator.

Right, but what if an attacker joins with fresh validators, after the existing validators have already lost some amount to leakage, and those fresh validators can survive longer before being ejected?

What do you mean?

Having a clear norm that "uint16 means shard ID" and "uint32 means validator index" could make it mentally easier to track what certain variables mean, and avoid accidentally using them in the wrong ways. That's all I meant?

Can we have a "messaging clean slate" after a fork, i.e. all pre-fork offchain messages are effectively invalidated and have to be rebroadcast?

No because that also breaks slashing.

I'd argue we do not want a global count limit:

OK, seems reasonable. We can replace the global count with per-object-type counts.

@vbuterin
Copy link
Contributor

Here's a PR for a per-type limit: #150

@vbuterin
Copy link
Contributor

And here's a PR for (4): #142

@vbuterin
Copy link
Contributor

For (9), the proof of custody PR already treats blocks as fixed-size. The one thing that it does not do is turn proofs of custody off if validator count is too low. Though I think we should consider a different path for that: a particular validator with index i does not need to participate in PoCs for shard s if hash(i, s) <= 2**256 * validator_count / FULLY_ENABLE_POCS_VALIDATOR_COUNT.

Thoughts @JustinDrake?

@JustinDrake
Copy link
Collaborator Author

JustinDrake commented Nov 20, 2018

we should consider a different path for that: a particular validator with index i does not need to participate in PoCs for shard s if hash(i, s) <= 2**256 * validator_count / FULLY_ENABLE_POCS_VALIDATOR_COUNT.

Oh, interesting! A few questions:

  • Should it be >= instead of <=? That is, the higher the validator_count the greater the probability that a validator must participate in the PoC.
  • What do you think of replacing hash(i, s) with hash(i, s, n) where n is the slot number to homogenise the workload across validators?
  • Should we replace the righthand side (currently linear with validator_count) with a function that more closely matches the validator workload (e.g. number of shards a validator is assigned to)?

@vbuterin
Copy link
Contributor

Should it be >= instead of <=? That is, the higher the validator_count the greater the probability that a validator must participate in the PoC.

No, <= gets the effect you want. If we want hash(i, s) <= N, then N being higher increases the chance that hash(i, s) will be below it.

What do you think of replacing hash(i, s) with hash(i, s, n) where n is the slot number to homogenise the workload across validators?

What slot number? A crosslink is not just for one slot, it's for data gathered over a sequence of slots. I suppose you could do the slot number of the previous crosslink...

Should we replace the righthand side (currently linear with validator_count) with a function that more closely matches the validator workload (e.g. number of shards a validator is assigned to)?

The expected number of shards a validator is assigned to is inversely proportional to validator count, so making the growth in proobability of selection be linear is exactly what's needed to counterbalance this and make the work constant, at least up until the point where validator_count == FULLY_ENABLE_POCS_VALIDATOR_COUNT.

@JustinDrake
Copy link
Collaborator Author

what if an attacker joins with fresh validators, after the existing validators have already lost some amount to leakage, and those fresh validators can survive longer before being ejected?

I don't completely understand the attack. Also, what prevents fresh validators that survive longer from registering in the current paradigm?

JustinDrake added a commit that referenced this issue Nov 24, 2018
See for example #128
@hwwhww
Copy link
Contributor

hwwhww commented Nov 25, 2018

FYI to other client implementers: "15. Use uint64 for shard number" commit is here: 5ba47b4

@djrtwo
Copy link
Contributor

djrtwo commented Nov 28, 2018

@JustinDrake Is this ready to be closed?

@JustinDrake
Copy link
Collaborator Author

Yes :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
general:enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants