Goal
Replace the transient EnvelopeProposer reject from #1120 with real per-proposer plumbing: EnvelopeProposerInstanceId, a fifth instance map, receive_data routing with a Gloas defense-in-depth gate, a fifth cleaner retain, and a QbftDecidable impl for EnvelopeConsensusData.
Context / motivation
Modeled on the ProposerConsensusData path (DutyExecutor::Validator), not the committee path. QbftManager currently holds four instance maps (qbft_manager/src/lib.rs:139-147 @ a9f378a4); the envelope value cannot reuse ProposerInstanceId because it has a different data type and no ValidatorDutyKind.
Suggested approach
- Import
EnvelopeConsensusData alongside the existing ssv_types::consensus values in qbft_manager/src/lib.rs.
EnvelopeProposerInstanceId { validator: PublicKeyBytes, instance_height: InstanceHeight } after ProposerInstanceId (qbft_manager/src/lib.rs:79); no duty field (one envelope duty per slot), no new ValidatorDutyKind variant.
- Fifth map +
DashMap::new() init (qbft_manager/src/lib.rs:139-147, qbft_manager/src/lib.rs:174-177).
- In
receive_data's DutyExecutor::Validator arm (qbft_manager/src/lib.rs:277-305): dedicated branch building the id and calling pass_to_instance::<EnvelopeConsensusData>, gated if !self.spec.fork_name_at_slot::<E>(slot).gloas_enabled() { return Err(QbftError::RoleNotActive); } (mirror the GloasBeaconVote gate at qbft_manager/src/lib.rs:321; spec field at qbft_manager/src/lib.rs:155). Remove the TODO(gloas) marker.
- Fifth
.retain(...) in the cleaner (qbft_manager/src/lib.rs:399-405).
impl<E: EthSpec> QbftDecidable<E> for EnvelopeConsensusData after the existing impls (qbft_manager/src/lib.rs:472-545): type Id = EnvelopeProposerInstanceId; message_id via MessageId::new(domain, Role::EnvelopeProposer, &DutyExecutor::Validator(id.validator)), mirroring the proposer impl.
- Keep
decide_instance generic. The Gloas check here gates inbound network messages; the local sign path added later must only initialize this instance after Gloas.
Acceptance criteria
- Five instance maps; Validator-executor
EnvelopeProposer routes to the new map; pre-Gloas rejects with RoleNotActive; Committee-executor still rejects with InconsistentMessageId.
- Post-Gloas routing inserts into
envelope_consensus_data_instances and does not touch proposer_consensus_data_instances.
- Cleaner retains the new map under the same TTL as siblings.
QbftDecidable produces a per-proposer message_id with Role::EnvelopeProposer and DutyExecutor::Validator(validator_pubkey).
- No
ValidatorDutyKind::EnvelopeProposer is added.
Tests
Routing tests: pre-Gloas reject, post-Gloas map insertion, Committee-executor reject, and QbftDecidable::message_id shape. Reuse the gloas_dispatch_tests map-size pattern where possible, and replace the transient-reject test from #1120. cargo test -p qbft_manager.
Notes
Issues are directionally correct, not prescriptive; verify symbols at PR time.
Goal
Replace the transient
EnvelopeProposerreject from #1120 with real per-proposer plumbing:EnvelopeProposerInstanceId, a fifth instance map,receive_datarouting with a Gloas defense-in-depth gate, a fifth cleaner retain, and aQbftDecidableimpl forEnvelopeConsensusData.Context / motivation
Modeled on the
ProposerConsensusDatapath (DutyExecutor::Validator), not the committee path.QbftManagercurrently holds four instance maps (qbft_manager/src/lib.rs:139-147@a9f378a4); the envelope value cannot reuseProposerInstanceIdbecause it has a different data type and noValidatorDutyKind.Suggested approach
EnvelopeConsensusDataalongside the existingssv_types::consensusvalues inqbft_manager/src/lib.rs.EnvelopeProposerInstanceId { validator: PublicKeyBytes, instance_height: InstanceHeight }afterProposerInstanceId(qbft_manager/src/lib.rs:79); nodutyfield (one envelope duty per slot), no newValidatorDutyKindvariant.DashMap::new()init (qbft_manager/src/lib.rs:139-147,qbft_manager/src/lib.rs:174-177).receive_data'sDutyExecutor::Validatorarm (qbft_manager/src/lib.rs:277-305): dedicated branch building the id and callingpass_to_instance::<EnvelopeConsensusData>, gatedif !self.spec.fork_name_at_slot::<E>(slot).gloas_enabled() { return Err(QbftError::RoleNotActive); }(mirror the GloasBeaconVote gate atqbft_manager/src/lib.rs:321;specfield atqbft_manager/src/lib.rs:155). Remove theTODO(gloas)marker..retain(...)in the cleaner (qbft_manager/src/lib.rs:399-405).impl<E: EthSpec> QbftDecidable<E> for EnvelopeConsensusDataafter the existing impls (qbft_manager/src/lib.rs:472-545):type Id = EnvelopeProposerInstanceId;message_idviaMessageId::new(domain, Role::EnvelopeProposer, &DutyExecutor::Validator(id.validator)), mirroring the proposer impl.decide_instancegeneric. The Gloas check here gates inbound network messages; the local sign path added later must only initialize this instance after Gloas.Acceptance criteria
EnvelopeProposerroutes to the new map; pre-Gloas rejects withRoleNotActive; Committee-executor still rejects withInconsistentMessageId.envelope_consensus_data_instancesand does not touchproposer_consensus_data_instances.QbftDecidableproduces a per-proposermessage_idwithRole::EnvelopeProposerandDutyExecutor::Validator(validator_pubkey).ValidatorDutyKind::EnvelopeProposeris added.Tests
Routing tests: pre-Gloas reject, post-Gloas map insertion, Committee-executor reject, and
QbftDecidable::message_idshape. Reuse thegloas_dispatch_testsmap-size pattern where possible, and replace the transient-reject test from #1120.cargo test -p qbft_manager.Notes
EnvelopeConsensusData).Issues are directionally correct, not prescriptive; verify symbols at PR time.