Thanks to @dankrad for giving input of the proposed solution.
tldr: (i) The SSZ object versioning is based on protocol versions, and (ii) heterogeneous list/vector is not allowed in SSZ. So with the current phase 1 spec (#1427), when we cross the version boundary, we are not able to process the previous operations with the new scheme.
Issue
Versioning in the networking layer
- For the individual operation gossip topics messages (e.g.,
attestation, voluntary_exit) are versioned by protocol IDs. So it's fine to receive messages of different versions at the single-operation level.
- The
Protocol ID would map to the specific schema version of the SSZ object definition, so that we can deserialize it with the corresponding schema. So 👍
Fork/version boundary operations
- However, when crossing the version boundary, the current
BeaconBlockBody.[operations]: List[Operation, N] only hold for one version of the given Operation.
- The block proposer cannot include both the previous version and current version objects in the same SSZ List. For example:
Example 1: Attestation inclusion
- In the phase 1 protocol changes, we can forecast that at least
AttestationData fields will be changed.
- For the attesters of the last slot of the previous version, if they create attestation with the old schema, their attestation will not be able to be included in the block of the first slot of the current version.
- If the attesters create attestation with the new schema, it's possible to be included in the next block. But, should they?
- Moreover, how about the late attestations?
Example 2: Slashing operations
AttesterSlashing contains attestation_1: IndexedAttestation and attestation_2: IndexedAttestation, so similar to Attestation, the new version block can't include heterogeneous AttesterSlashing formats. So we can't slash the malicious attesters at the previous version epoch.
ProposerSlashing contains header_1: BeaconBlockHeader and header_2: BeaconBlockHeader, which are the conflicting beacon block headers of the slot of the proposer to slash. If we change BeaconBlockHeader format in the future version, we can't slash the malicious proposer that double-proposed at the previous version epoch.
Example 3: BeaconState.previous_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH].
- At the first epoch of the new version, this field should be able to hold the previous version of
PendingAttestation.
Proposed solution
- Use SSZ
Union type to make Phase1 Lists compatable with both previous & current version of objects. e.g., Phase1BeaconBlockBody.attestations: List[Union[Phase0Attestation, Phase1Attestation], N].
- Pros:
- The proposers can now include both versions of SSZ objects.
- Cons:
- Compare to rejecting all incompatible operations, it's more complex to handle both versions.
Thanks to @dankrad for giving input of the proposed solution.
tldr: (i) The SSZ object versioning is based on protocol versions, and (ii) heterogeneous list/vector is not allowed in SSZ. So with the current phase 1 spec (#1427), when we cross the version boundary, we are not able to process the previous operations with the new scheme.
Issue
Versioning in the networking layer
attestation,voluntary_exit) are versioned by protocol IDs. So it's fine to receive messages of different versions at the single-operation level.Protocol IDwould map to the specific schema version of the SSZ object definition, so that we can deserialize it with the corresponding schema. So 👍Fork/version boundary operations
BeaconBlockBody.[operations]: List[Operation, N]only hold for one version of the givenOperation.Example 1: Attestation inclusion
AttestationDatafields will be changed.Example 2: Slashing operations
AttesterSlashingcontainsattestation_1: IndexedAttestationandattestation_2: IndexedAttestation, so similar toAttestation, the new version block can't include heterogeneousAttesterSlashingformats. So we can't slash the malicious attesters at the previous version epoch.ProposerSlashingcontainsheader_1: BeaconBlockHeaderandheader_2: BeaconBlockHeader, which are the conflicting beacon block headers of the slot of the proposer to slash. If we changeBeaconBlockHeaderformat in the future version, we can't slash the malicious proposer that double-proposed at the previous version epoch.Example 3:
BeaconState.previous_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH].PendingAttestation.Proposed solution
Uniontype to make Phase1 Lists compatable with both previous & current version of objects. e.g.,Phase1BeaconBlockBody.attestations: List[Union[Phase0Attestation, Phase1Attestation], N].