Problem
Height::decrement_by (crates/types/src/height.rs) returns Option<Self> but wraps saturating_sub in Some, so it never returns None — decrement_by(n) for n > height yields Some(Height(0)):
fn decrement_by(&self, n: u64) -> Option<Self> {
Some(Self(self.0.saturating_sub(n)))
}
The malachitebft Height trait documents this method as "Returns None if the height would be decremented below its minimum", and the same type's inherent decrement() already uses checked_sub. Since the trait's default decrement() delegates to decrement_by, the trait and inherent decrement() disagree at the zero boundary.
Proposed fix
Use checked_sub, matching the contract and the inherent decrement(). All call sites go through unwrap_or_default(), so behavior is unchanged. I have a patch with a test (fails on main, passes with the fix) ready once assigned.
Problem
Height::decrement_by(crates/types/src/height.rs) returnsOption<Self>but wrapssaturating_subinSome, so it never returnsNone—decrement_by(n)forn > heightyieldsSome(Height(0)):The
malachitebftHeighttrait documents this method as "Returns None if the height would be decremented below its minimum", and the same type's inherentdecrement()already useschecked_sub. Since the trait's defaultdecrement()delegates todecrement_by, the trait and inherentdecrement()disagree at the zero boundary.Proposed fix
Use
checked_sub, matching the contract and the inherentdecrement(). All call sites go throughunwrap_or_default(), so behavior is unchanged. I have a patch with a test (fails onmain, passes with the fix) ready once assigned.