Move EIP-3076 to Review status (#3248)

With EIP-3076 (slashing protection interchange) now implemented by the 4 major Eth2 clients (Lighthouse, Nimbus, Prysm, Teku), I would like to nominate it for review.

There are a few tweaks to the security recommendations that I would like to make before moving to Final Call, but the substance of the EIP has been stable for several months now and is unlikely to change.
This commit is contained in:
Michael Sproul 2021-02-09 17:16:24 +11:00 committed by GitHub
parent 8891c594b4
commit b0c5110212
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,50 +1,38 @@
---
eip: 3076
title: Validator client interchange format (slashing protection)
title: Slashing Protection Interchange Format
author: Michael Sproul (@michaelsproul), Sacha Saint-Leger (@sachayves), Danny Ryan (@djrtwo)
discussions-to: https://ethereum-magicians.org/t/eip-3076-validator-client-interchange-format-slashing-protection/
status: Draft
status: Review
type: Standards Track
category: Core
created: 2020-10-27
updated: 2021-02-08
---
## Simple Summary
A JSON interchange format for Serenity (eth2) that contains the necessary slashing protection information required to safely migrate keys between clients.
A JSON interchange format for proof of stake validators to migrate slashing protection data between clients.
## Abstract
A standard format for transferring a key's signing history allows validators to easily switch between clients without the risk of signing conflicting messages. While a [common keystore format](https://eips.ethereum.org/EIPS/eip-2335) provides part of the solution, it does not contain any information about a key's signing history. For a validator moving their keys from client A to client B, this can lead to scenarios in which client B inadvertently signs a message that conflicts with an earlier message signed with client A.
We propose a database interchange format that helps solve this problem. The format contains a record of all blocks and attestations signed by a set of validators.
A standard format for transferring a key's signing history allows validators to easily switch between clients without the risk of signing conflicting messages. While a [common keystore format](https://eips.ethereum.org/EIPS/eip-2335) provides part of the solution, it does not contain any information about a key's signing history. For a validator moving their keys from client A to client B, this could lead to scenarios in which client B inadvertently signs a message that conflicts with an earlier message signed with client A. The interchange format described here provides a solution to this problem.
## Motivation
Eth2 penalises validators for voting in ways that could result in two different versions of the chain being finalised. These types of penalties are called slashings.
The proof of stake (PoS) protocol penalises validators for voting in ways that could result in two different versions of the chain being finalised. These types of penalties are called slashings.
> The two main ways a validator can be slashed are
>
> 1. **Double voting**: voting for two different blocks during the same epoch.
> 2. **Surround voting**: voting for one version of reality, and later voting for another version in a way that doesn't make it clear that they no longer believe in the first.
For a validator following the protocol correctly, there is, in principle, no risk of being slashed. However, changing clients (from client A to client B, say) can result in a slashing risk if client B is unaware of the blocks and attestations that were signed with client A.
For a validator following the protocol correctly, there is, in principle, no risk of being slashed. However, changing clients (from client A to client B, say) can result in a slashing risk if client B is unaware of the blocks and attestations that were signed with client A.
Specifically, problems can occur if client A and client B do not agree on what the present time is (i.e if their clocks are not synchronised within a certain bound).
For example, say client A's time is accidentally set to a day in the future (225 epochs), and a validator switches from client A to client B without giving B a record of the blocks and attestations signed with A.
The validator in question now runs the risk of voting for two different blocks in the same epoch (a slashable offence) for the next 225 epochs (since they've already voted on these epochs with client A, and now stand to vote on them again with client B).
The above situation may sound far-fetched, but it's actually very similar to the incident that occurred on the eth2 Medalla testnet on 2020-08-14, where a time skew that affected all Prysm clients spiraled into a series of [cascading failures](https://medium.com/prysmatic-labs/eth2-medalla-testnet-incident-f7fbc3cc934a). It resulted in the testnet experiencing several days without finality -- an outcome that could have resulted in tens of millions of dollars in collective penalties had it occurred on mainnet.
This can can occur if client A and client B do not agree on what the present time is. For example, say client A's time is accidentally set to a day in the future (225 epochs), and a validator switches from client A to client B without giving B a record of the blocks and attestations signed with A. The validator in question now runs the risk of attesting to two different blocks in the same epoch (a slashable offence) for the next 225 epochs (since they've already voted on these epochs with client A, and now stand to vote on them again with client B). Such time-skew bugs have been observed in the wild.
Another situation in which slashing protection is critical is in the case of re-orgs. During a re-org it is possible for a validator to be assigned new attestation duties for an epoch in which it has already signed an attestation. In this case it is essential that the record of the previous attestation is available, even if the validator just moved from one client to another in the space of a single epoch.
## Specification
### JSON Schema
A valid interchange file is one that adheres to the following JSON schema:
A valid interchange file is one that adheres to the following JSON schema, and is interpreted according to the [Conditions](#conditions).
```json
{
@ -142,41 +130,40 @@ A valid interchange file is one that adheres to the following JSON schema:
]
}
```
### Example JSON Instance
```json
{
"metadata": {
"interchange_format_version": "5",
"genesis_validators_root": "0x04700007fabc8282644aed6d1c7c9e21d38a03a0c4ba193f3afe428824b3a673"
},
"data": [
"metadata": {
"interchange_format_version": "5",
"genesis_validators_root": "0x04700007fabc8282644aed6d1c7c9e21d38a03a0c4ba193f3afe428824b3a673"
},
"data": [
{
"pubkey": "0xb845089a1457f811bfc000588fbb4e713669be8ce060ea6be3c6ece09afc3794106c91ca73acda5e5457122d58723bed",
"signed_blocks": [
{
"pubkey": "0xb845089a1457f811bfc000588fbb4e713669be8ce060ea6be3c6ece09afc3794106c91ca73acda5e5457122d58723bed",
"signed_blocks": [
{
"slot": "81952",
"signing_root": "0x4ff6f743a43f3b4f95350831aeaf0a122a1a392922c45d804280284a69eb850b"
},
{
"slot": "81951",
}
],
"signed_attestations": [
{
"source_epoch": "2290",
"target_epoch": "3007",
"signing_root": "0x587d6a4f59a58fe24f406e0502413e77fe1babddee641fda30034ed37ecc884d"
},
{
"source_epoch": "2290",
"target_epoch": "3008",
}
]
"slot": "81952",
"signing_root": "0x4ff6f743a43f3b4f95350831aeaf0a122a1a392922c45d804280284a69eb850b"
},
{
"slot": "81951"
}
]
],
"signed_attestations": [
{
"source_epoch": "2290",
"target_epoch": "3007",
"signing_root": "0x587d6a4f59a58fe24f406e0502413e77fe1babddee641fda30034ed37ecc884d"
},
{
"source_epoch": "2290",
"target_epoch": "3008"
}
]
}
]
}
```
@ -210,72 +197,60 @@ target_epoch <=
- The `interchange_format_version` version is set to 5.
- A signed block or attestation's `signing_root` refers to the message data (hash tree root) that gets signed with a BLS signature. It allows validators to re-sign and re-broadcast blocks or attestations if asked.
- A signed block or attestation's `signing_root` refers to the message data (hash tree root) that gets signed with a BLS signature. It allows validators to re-sign and re-broadcast blocks or attestations if asked.
- The `signed_blocks` `signing_root`s are calculated using [`compute_signing_root(block, domain)`][csr]: where `block` is the block (of type `BeaconBlock` or `BeaconBlockHeader`) that was signed, and `domain` is equal to [`compute_domain(DOMAIN_BEACON_PROPOSER, fork, metadata.genesis_validators_root)`][cd].
- The `signed_attestations` `signing_root`s are calculated using [`compute_signing_root(attestation, domain)`][csr]: where `attestation` is the attestation (of type `AttestationData`) that was signed, and `domain` is equal to [`compute_domain(DOMAIN_BEACON_ATTESTER, fork, metadata.genesis_validators_root)`][cd].
[pps]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0-rc.0/specs/phase0/beacon-chain.md#proposer-slashings
[isad]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0-rc.0/specs/phase0/beacon-chain.md#is_slashable_attestation_data
[csr]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0-rc.0/specs/phase0/beacon-chain.md#compute_signing_root
[cd]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0-rc.0/specs/phase0/beacon-chain.md#compute_domain
[pps]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#proposer-slashings
[isad]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#is_slashable_attestation_data
[csr]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#compute_signing_root
[cd]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#compute_domain
## Rationale
The specification is designed to be flexible enough to support the full variety of slashing protection strategies that clients may implement.
### Supporting Different Strategies
An earlier design encompassed two separate formats to achieve the same goal: `complete` and `minimal` (for the complete changelog [see here](https://hackmd.io/@sproul/Bk0Y0qdGD)).
The interchange format is designed to be flexible enough to support the full variety of slashing protection strategies that clients may implement, which may be categorised into two main types:
The `complete` format was essentially the same as the format outlined in this document (without [conditions](#conditions) 2, 4 and 5). Its goal was to mimic the structure of a slashing protection database that prevents slashing by recording every message signed.
1. **Complete**: a database containing every message signed by each validator.
2. **Minimal**: a database containing only the latest messages signed by each validator.
The purpose of the `minimal` format was to allow for a simpler slashing protection strategy, whereby the only thing that needed to be recorded was the `slot` number of the most recently signed `block`, along with the `source` and `target` `epoch` numbers of the most recently signed attestation (while refusing to sign messages prior to these).
The advantage of the minimal strategy is its simplicity and succinctness. Using only the latest messages for each validator, safe slashing protection can be achieved by refusing to sign messages for slots or epochs prior.
> The advantage of the `complete` format was that it prevented false positives (meaning that it only prevented a validator from signing if it is guaranteed to incur a slashing otherwise). `complete` also supported republishing of identical messages if the `signing_root` is saved. The advantage of the `minimal` format was simplicity of implementation and succinctness of representation.
On the other hand, the complete strategy can provide safe slashing protection while also avoiding false positives (meaning that it only prevents a validator from signing if doing so would guarantee a slashing).
However, in the process of fleshing out this EIP, we realised that we could add a couple of [conditions](#conditions) to `complete` that would allow it to cover the `minimal` case entirely.
The two strategies are unified in the interchange format through the inclusion of [conditions](#conditions) (2), (4) and (5). This allows the interchange to transfer detailed or succinct information, as desired.
Whilst the format outlined in this specification is most similar to the old `complete`, clients who wish to follow a simpler slashing protection strategy (i.e. the old `minimal`), can do so by simply inserting a "fake" `signed_attestation` (with the correct `source_epoch` and `target_epoch` numbers), and a "fake" `signed_block` (with the correct `slot` number).
### Integer Representation
Most fields in the JSON schema are strings. For fields in which it is possible to encode the value as either a string or an integer, strings were chosen. This choice was made in order to avoid issues with different languages supporting different ranges of integers (specifically JavaScript, where the `number` type is a 64-bit float). If a validator is yet to sign a block or attestation, the relevant list is simply left empty.
Of the existing clients at time of writing, all clients are implementing `v4` (`"interchange_format_version": "4"`) of the specification outlined above.
### Versioning
The difference between `v4` and `v5` is simply cosmetic -- in `v5`, the `interchange_format` field has been removed from `metadata` because there is no longer a need to cater for two different formats.
The `interchange_format_version` is set to 5 because the specification went through several breaking changes during its design, incorporating feedback from implementers.
The `interchange_format_version` is set to 5 because the specification went through several breaking changes during its design, incorporating feedback from implementers.
## Backwards Compatibility
This specification is not backwards-compatible with versions 3 and 4 of the draft format that have been circulating amongst client teams the last few months. However, we expect clients will update to `v5` for mainnet launch.
This specification is not backwards-compatible with previous draft versions that used version numbers less than 5.
## Test Cases
The relevant test cases can be found in [this repository](https://github.com/eth2-clients/slashing-protection-interchange-tests).
## Implementation
Implementations exist for the following clients:
- [Teku](https://github.com/ConsenSys/teku/pull/2820)
- [Lighthouse](https://github.com/sigp/lighthouse/pull/1544)
- [Prysm](https://github.com/prysmaticlabs/prysm/pull/7518)
- [Nimbus](https://github.com/status-im/nimbus-eth2/pull/1643)
## Security Considerations
In order to minimise risk and complexity, the format has been designed to map cleanly onto the internal database formats used by implementers. Nevertheless, there are a few pitfalls worth illuminating.
### Advice for Complete-Record Databases
### Advice for Complete Databases
For implementers who use a complete record of signed messages to implement their slashing protection database, we make the following recommendations:
* You MUST ensure that, in addition to importing all of the messages from an interchange, all the [conditions](#conditions) are enforced. In particular, conditions (2), (4) and (5) may not have been enforced by your implementation before adopting the interchange format. Our recommendation is to enforce these rules at all times, to keep the implementation clean and minimise the attack surface. For example: your slashing protection mechanism should not sign a block with a slot number less than, or equal to, the minimum slot number of a previously signed block, _irrespective_ of whether that minimum-slot block was imported from an interchange file, or inserted as part of your database's regular operation.
* If your database records the signing roots of messages in addition to their slot/epochs, you should ensure that imported messages without signing roots are assigned a suitable dummy signing root internally. We suggest using a value like `0x0` which is extremely unlikely to collide with any real signing root.
* If your database records the signing roots of messages in addition to their slot/epochs, you should ensure that imported messages without signing roots are assigned a suitable dummy signing root internally. We suggest using a special "null" value which is distinct from all other signing roots, although a value like `0x0` may be used instead (as it is extremely unlikely to collide with any real signing root).
* Care must be taken to avoid signing messages within a gap in the database (an area of unknown signing activity). This could occur if two interchanges were imported with a large gap between the last entry of the first and the first entry of the second. Signing in this gap is not safe, and would violate conditions (2), (4) and (5). It can be avoided by storing an explicit low watermark in addition to the actual messages of the slashing protection database, or by pruning on import so that the oldest messages from the interchange become the oldest messages in the database.
### Advice for Succinct Databases
### Advice for Minimal Databases
For implementers who wish to implement their slashing protection database by storing only the latest block and attestation for each validator, we make the following recommendations:
@ -287,4 +262,5 @@ For implementers who wish to implement their slashing protection database by sto
* Similarly, your implementation should only allow an interchange file to be imported when the validator client is stopped.
## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).