EIP-3076 - Slashing Protection Interchange Format

Created 2020-10-27
Status Last Call
Category Interface
Type Standards Track
Authors

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 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

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.

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.

This 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, and is interpreted according to the Conditions.

{
  "title": "Signing history",
  "description": "This schema provides a record of the blocks and attestations signed by a set of validators",
  "type": "object",
  "properties": {
    "metadata": {
      "type": "object",
      "properties": {
        "interchange_format_version": {
          "type": "string",
          "description": "The version of the interchange format that this document adheres to"
        },
        "genesis_validators_root": {
          "type": "string",
          "description": "Calculated at Genesis time; serves to uniquely identify the chain"
        }
      },
      "required": [
        "interchange_format_version",
        "genesis_validators_root"
      ]
    },
    "data": {
      "type": "array",
      "items": [
        {
          "type": "object",
          "properties": {
            "pubkey": {
              "type": "string",
              "description": "The BLS public key of the validator (encoded as a 0x-prefixed hex string)"
            },
            "signed_blocks": {
              "type": "array",
              "items": [
                {
                  "type": "object",
                  "properties": {
                    "slot": {
                      "type": "string",
                      "description": "The slot number of the block that was signed"
                    },
                    "signing_root": {
                      "type": "string",
                      "description": "The output of compute_signing_root(block, domain)"
                    }
                  },
                  "required": [
                    "slot"
                  ]
                }
              ]
            },
            "signed_attestations": {
              "type": "array",
              "items": [
                {
                  "type": "object",
                  "properties": {
                    "source_epoch": {
                      "type": "string",
                      "description": "The attestation.data.source.epoch of the signed attestation"
                    },
                    "target_epoch": {
                      "type": "string",
                      "description": "The attestation.data.target.epoch of the signed attestation"
                    },
                    "signing_root": {
                      "type": "string",
                      "description": "The output of compute_signing_root(attestation, domain)"
                    }
                  },
                  "required": [
                    "source_epoch",
                    "target_epoch"
                  ]
                }
              ]
            }
          },
          "required": [
            "pubkey",
            "signed_blocks",
            "signed_attestations"
          ]
        }
      ]
    }
  },
  "required": [
    "metadata",
    "data"
  ]
}

Example JSON Instance

{
  "metadata": {
    "interchange_format_version": "5",
    "genesis_validators_root": "0x04700007fabc8282644aed6d1c7c9e21d38a03a0c4ba193f3afe428824b3a673"
  },
  "data": [
    {
      "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"
        }
      ]
    }
  ]
}

Conditions

After importing an interchange file with data field data, a signer must respect the following conditions:

  1. Refuse to sign any block that is slashable with respect to the blocks contained in data.signed_blocks. For details of what constitutes a slashable block, see process_proposer_slashing (from consensus-specs). If the signing_root is absent from a block, a signer must assume that any new block with the same slot is slashable with respect to the imported block.

  2. Refuse to sign any block with slot <= min(b.slot for b in data.signed_blocks if b.pubkey == proposer_pubkey), except if it is a repeat signing as determined by the signing_root.

  3. Refuse to sign any attestation that is slashable with respect to the attestations contained in data.signed_attestations. For details of what constitutes a slashable attestation, see is_slashable_attestation_data.

  4. Refuse to sign any attestation with source epoch less than the minimum source epoch present in that signer's attestations (as seen in data.signed_attestations). In pseudocode:

source.epoch <
    min(att.source_epoch
        for att in data.signed_attestations
        if att.pubkey == attester_pubkey)

{:start="5"} 5. Refuse to sign any attestation with target epoch less than or equal to the minimum target epoch present in that signer's attestations (as seen in data.signed_attestations), except if it is a repeat signing as determined by the signing_root. In pseudocode:

target_epoch <=
    min(att.target_epoch
        for att in data.signed_attestations
        if att.pubkey == attester_pubkey)

Additional Information

Rationale

Supporting Different Strategies

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:

  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 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.

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).

The two strategies are unified in the interchange format through the inclusion of conditions (2), (4) and (5). This allows the interchange to transfer detailed or succinct information, as desired.

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.

Versioning

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 previous draft versions that used version numbers less than 5.

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 Databases

For implementers who use a complete record of signed messages to implement their slashing protection database, we make the following recommendations:

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:

General Recommendations

Copyright

Copyright and related rights waived via CC0.