diff --git a/ouroboros-consensus/src/Ouroboros/Consensus/HardFork/History/Summary.hs b/ouroboros-consensus/src/Ouroboros/Consensus/HardFork/History/Summary.hs index 4de7f246432..d9f3a16f655 100644 --- a/ouroboros-consensus/src/Ouroboros/Consensus/HardFork/History/Summary.hs +++ b/ouroboros-consensus/src/Ouroboros/Consensus/HardFork/History/Summary.hs @@ -494,8 +494,35 @@ instance SListI xs => Serialise (Summary xs) where encode (Summary eraSummaries) = encode (toList eraSummaries) decode = do eraSummaries <- decode - case Summary <$> nonEmptyFromList eraSummaries of + -- The sender might be running a newer version that is (statically) aware + -- of more eras (@xs@) than we are. In this case, we ignore the eras past + -- the final era we are aware of rather than failing to decode. + let knownEraSummaries = take numKnownEras eraSummaries + + -- When we received as many summaries as known eras, the final end + -- bound must already be unbounded, in which case this is a no-op. + -- + -- When we actually had to drop some unknown eras, the now final era + -- will have a bounded end bound, as it was followed by one or more + -- eras. In this case, we make that end bound unbounded, as is always + -- the case for the final era. + go | length knownEraSummaries == numKnownEras + = fixEndBound + + -- Fewer end summaries than known eras, by definition the last one + -- will /not/ have an unbounded end bound. We don't have to modify. + | otherwise + = id + + case Summary . go <$> nonEmptyFromList eraSummaries of Just summary -> return summary - Nothing -> fail $ - "Summary: expected between 1 and " <> show (lengthSList (Proxy @xs)) <> - " eras but got " <> show (length eraSummaries) + Nothing -> fail "Summary: expected at least one era summary" + where + -- | The number of statically known eras. + numKnownEras :: Int + numKnownEras = lengthSList (Proxy @xs) + + -- | Make the last era's end bound unbounded. + fixEndBound :: NonEmpty xs' EraSummary -> NonEmpty xs' EraSummary + fixEndBound (NonEmptyCons e es) = NonEmptyCons e (fixEndBound es) + fixEndBound (NonEmptyOne e) = NonEmptyOne e { eraEnd = EraUnbounded }