Guard the lens draw's divisor directly (L1 c1.s1.f2)

The draw checked `draws()` over the map's values and then divided by a
sum over `Lens::ALL`. Those were two computations that happened to agree:
weakening the guard alone reached a modulo by zero. The draw now sums the
total first and refuses a zero total, as `select_band` does; `draws()`
stays the admission rule and no longer guards the draw.

Builds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-16 15:53:04 +02:00
commit dfb3c52b01

View file

@ -186,7 +186,13 @@ pub(crate) fn draw(
world_id: WorldId,
command_id: CommandId,
) -> Result<Lens, KernelError> {
if !weights.draws() {
// The guard is on the divisor itself, as `select_band`'s is: no other
// statement of "these weights draw" can let the modulo below divide by zero.
let total: u128 = Lens::ALL
.iter()
.map(|lens| u128::from(weights.get(*lens)))
.sum();
if total == 0 {
return Err(KernelError::Invariant("lens weights never draw".into()));
}
let preimage = digest(&LensPreimage {
@ -198,10 +204,6 @@ pub(crate) fn draw(
.and_then(|hex| hex.get(..16))
.and_then(|head| u64::from_str_radix(head, 16).ok())
.ok_or_else(|| KernelError::Invariant("lens draw digest is not hex".into()))?;
let total: u128 = Lens::ALL
.iter()
.map(|lens| u128::from(weights.get(*lens)))
.sum();
let mut position = u128::from(head) % total;
for lens in Lens::ALL {
let weight = u128::from(weights.get(lens));