# Slashing Protection Database Interchange Format ## Changelog It is recommended to only support the latest version of the spec. **This document has been superseded by EIP-3076, available here:** https://eips.ethereum.org/EIPS/eip-3076 v5 (2020-10-23): * Remove redundant `interchange_format` field * Final draft before EIP v4 (2020-09-17): * Replace `minimal` format by applying new semantics to the `complete` format v3 (2020-09-01): * Allow `last_signed_block_slot` and `last_signed_attestation_{source,target}_epoch` to be `null` in the minimal format, to represent the case where a validator has not yet signed a block or attestation (thanks Paul Harris). v2 (2020-08-31): * Require all integers/slots/epochs to be quoted so that JavaScript compatibility issues are avoided v1 (2020-08-18): * Initial version ## Complete Format A complete record of all blocks and attestations signed by a set of validators ```json { "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", }, ... ] } ] } ``` * `pubkey: BLSPubkey` is the BLS public key of the validator encoded as 0x-prefixed hex. * `signed_blocks` is a list of objects with fields: * `slot: Slot`, the slot of the block that was signed * `signing_root: Root` (optional) is [`compute_signing_root(block, domain)`][csr], where: * `block` is the block that was signed as type `BeaconBlock` or equivalently `BeaconBlockHeader` * `domain` is equal to [`compute_domain(DOMAIN_BEACON_PROPOSER, fork, metadata.genesis_validators_root)`][cd], where: * `metadata.genesis_validators_root` is the `genesis_validators_root` from this interchange file * `fork: Version` is the fork that the block was signed against * `signed_attestations` is a list of objects with fields: * `source_epoch: Epoch`, the `attestation.data.source.epoch` of the signed attestation * `target_epoch: Epoch`, the `attestation.data.target.epoch` of the signed attestation * `signing_root: Root` (optional) is [`compute_signing_root(attestation, domain)`][csr], where: * `attestation` is the attestation that was signed as type `AttestationData` * `domain` is equal to [`compute_domain(DOMAIN_BEACON_ATTESTER, fork, metadata.genesis_validators_root)`][cd], where: * `metadata.genesis_validators_root` is the `genesis_validators_root` from this interchange file * `fork: Version` is the fork that the attestation was signed against ### Semantics To support the role that the `minimal` format used to play, we impose some additional restrictions about signing messages less than the minimum block/attestation (points 2 and 4 below). After importing an interchange file with data field `data`, a signer MUST: 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][pps]. 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][isad]. 4. Refuse to sign any attestation with: - `source.epoch < min(att.source_epoch for att in data.signed_attestations if att.pubkey == attester_pubkey)`, OR - `target_epoch <= min(att.target_epoch for att in data.signed_attestations if att.pubkey == attester_pubkey)` The old `minimal` format can be emulated by outputting 0-1 attestations and blocks for each validator (no signing root required). For example, this `complete` interchange is now equivalent to the old `minimal` example shown below: ```json { "metadata": { "interchange_format": "complete", "interchange_format_version": "4", "genesis_validators_root": "0x04700007fabc8282644aed6d1c7c9e21d38a03a0c4ba193f3afe428824b3a673" }, "data": [ { "pubkey": "0xb845089a1457f811bfc000588fbb4e713669be8ce060ea6be3c6ece09afc3794106c91ca73acda5e5457122d58723bed", "signed_blocks": [ { "slot": "89765" } ], "signed_attestations": [ { "source_epoch": "2990", "target_epoch": "3007" } ] }, ... ], } ``` If a validator is yet to sign a block or attestation, the relevant list is simply left empty -- no more `null`! [pps]: https://github.com/ethereum/eth2.0-specs/blob/v0.12.2/specs/phase0/beacon-chain.md#proposer-slashings [isad]: https://github.com/ethereum/eth2.0-specs/blob/v0.12.2/specs/phase0/beacon-chain.md#is_slashable_attestation_data [csr]: https://github.com/ethereum/eth2.0-specs/blob/v0.12.2/specs/phase0/beacon-chain.md#compute_signing_root [cd]: https://github.com/ethereum/eth2.0-specs/blob/v0.12.2/specs/phase0/beacon-chain.md#compute_domain ## Old Minimal Format (v3) NOT SUPPORTED AS OF v4 DRAFT OR LATER Similar to Teku's native format, just 3 integers per validator: ```json { "metadata": { "interchange_format": "minimal", "interchange_format_version": "3", "genesis_validators_root": "0x04700007fabc8282644aed6d1c7c9e21d38a03a0c4ba193f3afe428824b3a673" }, "data": [ { "pubkey": "0xb845089a1457f811bfc000588fbb4e713669be8ce060ea6be3c6ece09afc3794106c91ca73acda5e5457122d58723bed", "last_signed_block_slot": "89765", "last_signed_attestation_source_epoch": "2990", "last_signed_attestation_target_epoch": "3007", }, ... ], } ``` * `pubkey` is the BLS public key of the validator encoded as 0x-prefixed hex. * `last_signed_block_slot: Slot` (optional) is the greatest slot of a block signed by `pubkey`. Can be `null` to indicate that no block has been signed. * `last_signed_attestation_source_epoch: Epoch` (optional) is the greatest `attestation.data.source.epoch` of an `attestation` signed by `pubkey`. Can be `null` or absent. * `last_signed_attestation_target_epoch: Epoch` (optional) is the greatest `attestation.data.target.epoch` of an `attestation` signed by `pubkey`. Can be `null` or absent. It is an error for one of `last_signed_attestation_source_epoch` or `last_signed_attestation_target_epoch` to be `null`/absent, if the other is present. ## Test Cases https://github.com/eth2-clients/slashing-protection-interchange-tests ## Possible Changes Some breaking changes that could be worth pursuing with client consensus * ~~Rename `last_signed_` fields to `greatest_signed_` or `highest_signed_` to clarify that it should not be the most _recently_ signed message, but the highest slot/epoch one.~~ * Hard-fork future proofing (list fork versions and slots in metadata) * A hybrid of the minimal and complete formats that prevents signing messages before some point, with complete data for more recent epochs (DONE, this is the new v4 complete format) ## References * Lighthouse's slashing protection DB schema: https://github.com/sigp/lighthouse/blob/9a97a0b14fdcd265769981a02e9cb37dac3b553a/validator_client/slashing_protection/src/slashing_database.rs#L52-L81 * Lighthouse's implementation of the format: https://github.com/sigp/lighthouse/pull/1544 * Lighthouse docs on import/export: https://lighthouse-book.sigmaprime.io/slashing-protection.html#import-and-export * SPDIF not to be confused with [S/PDIF](https://en.wikipedia.org/wiki/S/PDIF)