Merge pull request #1615 from ethereum/subnet-validations

Add subnet validations for DoS resistance
This commit is contained in:
Danny Ryan 2020-02-12 12:02:28 -07:00 committed by GitHub
commit a9fae27379
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 74 additions and 31 deletions

View File

@ -143,6 +143,8 @@ DOMAIN_BEACON_ATTESTER: 0x01000000
DOMAIN_RANDAO: 0x02000000 DOMAIN_RANDAO: 0x02000000
DOMAIN_DEPOSIT: 0x03000000 DOMAIN_DEPOSIT: 0x03000000
DOMAIN_VOLUNTARY_EXIT: 0x04000000 DOMAIN_VOLUNTARY_EXIT: 0x04000000
DOMAIN_SELECTION_PROOF: 0x05000000
DOMAIN_AGGREGATE_AND_PROOF: 0x06000000
# Phase 1 # Phase 1
DOMAIN_SHARD_PROPOSAL: 0x80000000 DOMAIN_SHARD_PROPOSAL: 0x80000000
DOMAIN_SHARD_COMMITTEE: 0x81000000 DOMAIN_SHARD_COMMITTEE: 0x81000000

View File

@ -142,6 +142,8 @@ DOMAIN_BEACON_ATTESTER: 0x01000000
DOMAIN_RANDAO: 0x02000000 DOMAIN_RANDAO: 0x02000000
DOMAIN_DEPOSIT: 0x03000000 DOMAIN_DEPOSIT: 0x03000000
DOMAIN_VOLUNTARY_EXIT: 0x04000000 DOMAIN_VOLUNTARY_EXIT: 0x04000000
DOMAIN_SELECTION_PROOF: 0x05000000
DOMAIN_AGGREGATE_AND_PROOF: 0x06000000
# Phase 1 # Phase 1
DOMAIN_SHARD_PROPOSAL: 0x80000000 DOMAIN_SHARD_PROPOSAL: 0x80000000
DOMAIN_SHARD_COMMITTEE: 0x81000000 DOMAIN_SHARD_COMMITTEE: 0x81000000

View File

@ -253,11 +253,14 @@ The following values are (non-configurable) constants used throughout the specif
| Name | Value | | Name | Value |
| - | - | | - | - |
| `DOMAIN_BEACON_PROPOSER` | `DomainType('0x00000000')` | | `DOMAIN_BEACON_PROPOSER` | `DomainType('0x00000000')` |
| `DOMAIN_BEACON_ATTESTER` | `DomainType('0x01000000')` | | `DOMAIN_BEACON_ATTESTER` | `DomainType('0x01000000')` |
| `DOMAIN_RANDAO` | `DomainType('0x02000000')` | | `DOMAIN_RANDAO` | `DomainType('0x02000000')` |
| `DOMAIN_DEPOSIT` | `DomainType('0x03000000')` | | `DOMAIN_DEPOSIT` | `DomainType('0x03000000')` |
| `DOMAIN_VOLUNTARY_EXIT` | `DomainType('0x04000000')` | | `DOMAIN_VOLUNTARY_EXIT` | `DomainType('0x04000000')` |
| `DOMAIN_SELECTION_PROOF` | `DomainType('0x05000000')` |
| `DOMAIN_AGGREGATE_AND_PROOF` | `DomainType('0x06000000')` |
## Containers ## Containers

View File

@ -229,15 +229,15 @@ where `base64` is the [URL-safe base64 alphabet](https://tools.ietf.org/html/rfc
The payload is carried in the `data` field of a gossipsub message, and varies depending on the topic: The payload is carried in the `data` field of a gossipsub message, and varies depending on the topic:
| Topic | Message Type | | Topic | Message Type |
|------------------------------------------------|----------------------| |------------------------------------------------|-------------------------|
| beacon_block | SignedBeaconBlock | | beacon_block | SignedBeaconBlock |
| beacon_aggregate_and_proof | AggregateAndProof | | beacon_aggregate_and_proof | SignedAggregateAndProof |
| beacon_attestation\* | Attestation | | beacon_attestation\* | Attestation |
| committee_index{subnet_id}\_beacon_attestation | Attestation | | committee_index{subnet_id}\_beacon_attestation | Attestation |
| voluntary_exit | SignedVoluntaryExit | | voluntary_exit | SignedVoluntaryExit |
| proposer_slashing | ProposerSlashing | | proposer_slashing | ProposerSlashing |
| attester_slashing | AttesterSlashing | | attester_slashing | AttesterSlashing |
Clients MUST reject (fail validation) messages containing an incorrect type, or invalid payload. Clients MUST reject (fail validation) messages containing an incorrect type, or invalid payload.
@ -250,16 +250,19 @@ When processing incoming gossip, clients MAY descore or disconnect peers who fai
There are two primary global topics used to propagate beacon blocks and aggregate attestations to all nodes on the network. Their `TopicName`s are: There are two primary global topics used to propagate beacon blocks and aggregate attestations to all nodes on the network. Their `TopicName`s are:
- `beacon_block` - This topic is used solely for propagating new signed beacon blocks to all nodes on the networks. Signed blocks are sent in their entirety. The following validations MUST pass before forwarding the `signed_beacon_block` on the network - `beacon_block` - This topic is used solely for propagating new signed beacon blocks to all nodes on the networks. Signed blocks are sent in their entirety. The following validations MUST pass before forwarding the `signed_beacon_block` on the network
- The proposer signature, `signed_beacon_block.signature` is valid.
- The block is not from a future slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. validate that `signed_beacon_block.message.slot <= current_slot` (a client MAY queue future blocks for processing at the appropriate slot). - The block is not from a future slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. validate that `signed_beacon_block.message.slot <= current_slot` (a client MAY queue future blocks for processing at the appropriate slot).
- `beacon_aggregate_and_proof` - This topic is used to propagate aggregated attestations (as `AggregateAndProof`s) to subscribing nodes (typically validators) to be included in future blocks. The following validations MUST pass before forwarding the `aggregate_and_proof` on the network. - The block is the first block received for the proposer for the slot, `signed_beacon_block.message.slot`.
- The aggregate attestation defined by `hash_tree_root(aggregate_and_proof.aggregate)` has _not_ already been seen (via aggregate gossip, within a block, or through the creation of an equivalent aggregate locally). - The proposer signature, `signed_beacon_block.signature`, is valid.
- The block being voted for (`aggregate_and_proof.aggregate.data.beacon_block_root`) passes validation. - `beacon_aggregate_and_proof` - This topic is used to propagate aggregated attestations (as `SignedAggregateAndProof`s) to subscribing nodes (typically validators) to be included in future blocks. The following validations MUST pass before forwarding the `signed_aggregate_and_proof` on the network. (We define the following for convenience -- `aggregate_and_proof = signed_aggregate_and_proof.message` and `aggregate = aggregate_and_proof.aggregate`)
- `aggregate_and_proof.aggregate.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `aggregate_and_proof.aggregate.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= aggregate_and_proof.aggregate.data.slot`. - `aggregate.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `aggregate.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= aggregate.data.slot` (a client MAY queue future aggregates for processing at the appropriate slot).
- The validator index is within the aggregate's committee -- i.e. `aggregate_and_proof.aggregator_index in get_attesting_indices(state, aggregate_and_proof.aggregate.data, aggregate_and_proof.aggregate.aggregation_bits)`. - The aggregate attestation defined by `hash_tree_root(aggregate)` has _not_ already been seen (via aggregate gossip, within a block, or through the creation of an equivalent aggregate locally).
- `aggregate_and_proof.selection_proof` selects the validator as an aggregator for the slot -- i.e. `is_aggregator(state, aggregate_and_proof.aggregate.data.slot, aggregate_and_proof.aggregate.data.index, aggregate_and_proof.selection_proof)` returns `True`. - The `aggregate` is the first aggregate received for the aggregator with index `aggregate_and_proof.aggregator_index` for the slot `aggregate.data.slot`.
- The `aggregate_and_proof.selection_proof` is a valid signature of the `aggregate_and_proof.aggregate.data.slot` by the validator with index `aggregate_and_proof.aggregator_index`. - The block being voted for (`aggregate.data.beacon_block_root`) passes validation.
- The signature of `aggregate_and_proof.aggregate` is valid. - `aggregate_and_proof.selection_proof` selects the validator as an aggregator for the slot -- i.e. `is_aggregator(state, aggregate.data.slot, aggregate.data.index, aggregate_and_proof.selection_proof)` returns `True`.
- The aggregator's validator index is within the aggregate's committee -- i.e. `aggregate_and_proof.aggregator_index in get_attesting_indices(state, aggregate.data, aggregate.aggregation_bits)`.
- The `aggregate_and_proof.selection_proof` is a valid signature of the `aggregate.data.slot` by the validator with index `aggregate_and_proof.aggregator_index`.
- The aggregator signature, `signed_aggregate_and_proof.signature`, is valid.
- The signature of `aggregate` is valid.
Additional global topics are used to propagate lower frequency validator messages. Their `TopicName`s are: Additional global topics are used to propagate lower frequency validator messages. Their `TopicName`s are:
@ -273,9 +276,10 @@ Attestation subnets are used to propagate unaggregated attestations to subsectio
- `committee_index{subnet_id}_beacon_attestation` - These topics are used to propagate unaggregated attestations to the subnet `subnet_id` (typically beacon and persistent committees) to be aggregated before being gossiped to `beacon_aggregate_and_proof`. The following validations MUST pass before forwarding the `attestation` on the subnet. - `committee_index{subnet_id}_beacon_attestation` - These topics are used to propagate unaggregated attestations to the subnet `subnet_id` (typically beacon and persistent committees) to be aggregated before being gossiped to `beacon_aggregate_and_proof`. The following validations MUST pass before forwarding the `attestation` on the subnet.
- The attestation's committee index (`attestation.data.index`) is for the correct subnet. - The attestation's committee index (`attestation.data.index`) is for the correct subnet.
- `attestation.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots (within a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `attestation.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= attestation.data.slot` (a client MAY queue future attestations for processing at the appropriate slot).
- The attestation is unaggregated -- that is, it has exactly one participating validator (`len([bit for bit in attestation.aggregation_bits if bit == 0b1]) == 1`). - The attestation is unaggregated -- that is, it has exactly one participating validator (`len([bit for bit in attestation.aggregation_bits if bit == 0b1]) == 1`).
- The attestation is the first attestation received for the participating validator for the slot, `attestation.data.slot`.
- The block being voted for (`attestation.data.beacon_block_root`) passes validation. - The block being voted for (`attestation.data.beacon_block_root`) passes validation.
- `attestation.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots (within a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `attestation.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= attestation.data.slot`.
- The signature of `attestation` is valid. - The signature of `attestation` is valid.
#### Interop #### Interop

View File

@ -59,6 +59,7 @@
- [Aggregate signature](#aggregate-signature-1) - [Aggregate signature](#aggregate-signature-1)
- [Broadcast aggregate](#broadcast-aggregate) - [Broadcast aggregate](#broadcast-aggregate)
- [`AggregateAndProof`](#aggregateandproof) - [`AggregateAndProof`](#aggregateandproof)
- [`SignedAggregateAndProof`](#signedaggregateandproof)
- [Phase 0 attestation subnet stability](#phase-0-attestation-subnet-stability) - [Phase 0 attestation subnet stability](#phase-0-attestation-subnet-stability)
- [How to avoid slashing](#how-to-avoid-slashing) - [How to avoid slashing](#how-to-avoid-slashing)
- [Proposer slashing](#proposer-slashing) - [Proposer slashing](#proposer-slashing)
@ -423,7 +424,7 @@ A validator is selected to aggregate based upon the return value of `is_aggregat
```python ```python
def get_slot_signature(state: BeaconState, slot: Slot, privkey: int) -> BLSSignature: def get_slot_signature(state: BeaconState, slot: Slot, privkey: int) -> BLSSignature:
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, compute_epoch_at_slot(slot)) domain = get_domain(state, DOMAIN_SELECTION_PROOF, compute_epoch_at_slot(slot))
signing_root = compute_signing_root(slot, domain) signing_root = compute_signing_root(slot, domain)
return bls.Sign(privkey, signing_root) return bls.Sign(privkey, signing_root)
``` ```
@ -461,9 +462,37 @@ def get_aggregate_signature(attestations: Sequence[Attestation]) -> BLSSignature
#### Broadcast aggregate #### Broadcast aggregate
If the validator is selected to aggregate (`is_aggregator`), then they broadcast their best aggregate to the global aggregate channel (`beacon_aggregate_and_proof`) two-thirds of the way through the `slot`-that is, `SECONDS_PER_SLOT * 2 / 3` seconds after the start of `slot`. If the validator is selected to aggregate (`is_aggregator`), then they broadcast their best aggregate as a `SignedAggregateAndProof` to the global aggregate channel (`beacon_aggregate_and_proof`) two-thirds of the way through the `slot`-that is, `SECONDS_PER_SLOT * 2 / 3` seconds after the start of `slot`.
Aggregate attestations are broadcast as `AggregateAndProof` objects to prove to the gossip channel that the validator has been selected as an aggregator. Selection proofs are provided in `AggregateAndProof` to prove to the gossip channel that the validator has been selected as an aggregator.
`AggregateAndProof` messages are signed by the aggregator and broadcast inside of `SignedAggregateAndProof` objects to prevent a class of DoS attacks and message forgeries.
First, `aggregate_and_proof = get_aggregate_and_proof(state, validator_index, aggregate_attestation, privkey)` is constructed.
```python
def get_aggregate_and_proof(state: BeaconState,
aggregator_index: ValidatorIndex,
aggregate: Attestation,
privkey: int) -> AggregateAndProof:
return AggregateAndProof(
aggregator_index=aggregator_index,
aggregate=aggregate,
selection_proof=get_slot_signature(state, aggregate.data.slot, privkey),
)
```
Then `signed_aggregate_and_proof = SignedAggregateAndProof(message=aggregate_and_proof, signature=signature)` is constructed and broadast. Where `signature` is obtained from:
```python
def get_aggregate_and_proof_signature(state: BeaconState,
aggregate_and_proof: AggregateAndProof,
privkey: int) -> BLSSignature:
aggregate = aggregate_and_proof.aggregate
domain = get_domain(state, DOMAIN_AGGREGATE_AND_PROOF, compute_epoch_at_slot(aggregate.data.slot))
signing_root = compute_signing_root(aggregate_and_proof, domain)
return bls.Sign(privkey, signing_root)
```
##### `AggregateAndProof` ##### `AggregateAndProof`
@ -474,10 +503,13 @@ class AggregateAndProof(Container):
selection_proof: BLSSignature selection_proof: BLSSignature
``` ```
Where ##### `SignedAggregateAndProof`
* `aggregator_index` is the validator's `ValidatorIndex`.
* `aggregate` is the `aggregate_attestation` constructed in the previous section. ```python
* `selection_proof` is the signature of the slot (`get_slot_signature()`). class SignedAggregateAndProof(Container):
message: AggregateAndProof
signature: BLSSignature
```
## Phase 0 attestation subnet stability ## Phase 0 attestation subnet stability