From af2fcac44421c7ec6abcb0c6c7b48e217bb4bdca Mon Sep 17 00:00:00 2001 From: Devnet Bot Date: Mon, 18 May 2026 22:14:33 +0000 Subject: [PATCH] fix(focil): align should_extend_payload with spec for gloas/heze MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous implementation unconditionally checked is_payload_inclusion_list_satisfied at the top of should_extend_payload, returning false when the root was not in the map. Since the map was only populated for heze slots (FOCIL), this caused should_extend_payload to always return false for gloas slots — making the tiebreaker favor Empty over Full for every single slot. Per spec: - Gloas: should_extend_payload checks is_payload_verified (envelope received) then timeliness/proposer conditions. No IL check. - Heze: adds is_payload_inclusion_list_satisfied as an additional gate. Fix: check payload_received first (matching is_payload_verified), then only gate on IL satisfaction if the root IS present in the map (heze slots where it gets recorded). For gloas slots where no IL exists, the check is skipped entirely. --- consensus/proto_array/src/proto_array.rs | 25 +++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/consensus/proto_array/src/proto_array.rs b/consensus/proto_array/src/proto_array.rs index 4dc1232bc8..605403e177 100644 --- a/consensus/proto_array/src/proto_array.rs +++ b/consensus/proto_array/src/proto_array.rs @@ -1509,17 +1509,28 @@ impl ProtoArray { proto_node: &ProtoNode, proposer_boost_root: Hash256, ) -> Result { - // Per spec: is_payload_inclusion_list_satisfied returns false unless the - // root is in the map AND the value is true. - if !self - .payload_inclusion_list_satisfaction - .get(&fc_node.root) - .copied() - .unwrap_or(false) + // Per spec (Gloas): if not is_payload_verified(store, root): return False + // In our implementation, payload_received == True means the envelope was received. + if !proto_node + .payload_received() + .map_err(|_| Error::InvalidNodeVariant { block_root: fc_node.root })? { return Ok(false); } + // Per spec (Heze/FOCIL): is_payload_inclusion_list_satisfied check. + // Only applies when the IL satisfaction map has entries (i.e., heze is active). + // For gloas-only slots where no IL exists, the map won't contain the root, + // but we should NOT gate on this — only check if the root IS in the map. + if let Some(&satisfied) = self + .payload_inclusion_list_satisfaction + .get(&fc_node.root) + { + if !satisfied { + return Ok(false); + } + } + // Per spec: `proposer_root == Root()` is one of the `or` conditions that // makes `should_extend_payload` return True. if proposer_boost_root.is_zero() {