module hotel open util/ordering [Time] sig Key, Time {} sig Card { fst, snd: Key } sig Room { key: Key one -> Time } one sig Desk { prev: (Room -> lone Key) -> Time, issued: Key -> Time, occ: (Room -> lone Guest) -> Time } sig Guest { cards: Card -> Time } pred init (t: Time) { Desk.prev.t = key.t -- no issued.t and no cards.t Desk.issued.t = Room.key.t and no cards.t and no occ.t } pred checkin (t, t': Time, r: Room, g: Guest) { no r.(Desk.occ.t) Desk.occ.t' = Desk.occ.t + r ->g some c: Card { c.fst = r.(Desk.prev.t) c.snd not in Desk.issued.t cards.t' = cards.t + g -> c Desk.issued.t' = Desk.issued.t + c.snd Desk.prev.t' = Desk.prev.t ++ r -> c.snd } key.t = key.t' } pred checkout (t, t': Time, r: Room, g: Guest) { Desk.occ.t' = Desk.occ.t - r ->g issued.t = issued.t' and prev.t = prev.t' cards.t = cards.t' } pred enter (t, t': Time, r: Room, g: Guest) { some c: g.cards.t | let k = r.key.t { c.snd = k and key.t' = key.t or c.fst = k and key.t' = key.t ++ r -> c.snd } issued.t = issued.t' and prev.t = prev.t' and occ.t = occ.t' cards.t = cards.t' } fact Traces { init (first()) all t: Time - last () | some g: Guest, r: Room | checkin (t, next(t), r, g) or enter (t, next(t), r, g) } assert NoIntruder { no t1: Time, disj g, g': Guest, r: Room | let t2 = next (t1), t3 = next (t2) | enter (t1, t2, r, g) and enter (t2, t3, r, g') } check NoIntruder for 3 but 5 Time -- exercise: give guest ability to make new cards from keys she has