Skip to content

Commit 9cf1bd1

Browse files
committed
docs(design): update design doc to the expires_at model
Reflect the refactor from last_used + read-time TTL to a stored expires_at (stamped now+TTL at write time), and note the migration now also stamps expiry on legacy mocks (main #3785).
1 parent 503a627 commit 9cf1bd1

1 file changed

Lines changed: 23 additions & 16 deletions

File tree

docs/design/auto-remove-launcher-hashes-design.md

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,17 @@ without any vote.
2323
pub struct AllowedLauncherImage {
2424
launcher_hash: LauncherImageHash,
2525
compose_hashes: Vec<LauncherDockerComposeHash>,
26-
last_used: Timestamp, // NEW: stamped when voted in / re-voted, and refreshed on
27-
// each attestation by a *current participant*.
26+
expires_at: Timestamp, // NEW: stamped `now + TTL` when voted in / re-voted, and
27+
// restamped on each attestation by a *current participant*.
2828
}
2929
```
3030

31-
An entry is **expired** when `last_used + TTL < now`.
31+
An entry is **expired** when `expires_at < now`. The expiry is computed once at write
32+
time (`expires_at = now + TTL`, saturating on overflow) — mirroring how attestations store
33+
their own expiry — so reads are a plain `expires_at < now` comparison and the TTL is only
34+
needed at the write sites (vote-in, refresh, migration), not threaded through every read.
35+
A consequence: changing the TTL config applies to an entry on its *next* stamp (a node
36+
resubmits hourly), not retroactively.
3237
TTL is a new config field `launcher_hash_unused_ttl_seconds`, default **14 days**.
3338
Config validation enforces `launcher_hash_unused_ttl_seconds >=
3439
mpc_attestation::attestation::DEFAULT_EXPIRATION_DURATION_SECONDS` (the
@@ -40,7 +45,7 @@ period, 7 days).
4045
Three parts:
4146

4247
1. **Refresh on use** — after a successful verify in `submit_participant_info`
43-
(hourly per node), the contract sets `last_used = now` on the entry
48+
(hourly per node), the contract restamps `expires_at = now + TTL` on the entry
4449
owning the validated compose hash. No node-side changes.
4550
**Only a current participant's submission refreshes the timestamp.**
4651
`submit_participant_info` is also callable by prospective (non-participant)
@@ -64,19 +69,19 @@ Three parts:
6469
`clean_tee_status_tera_gas` etc. used by the post-resharing cleanups in
6570
`vote_reshared`.
6671

67-
Stamping `last_used` at vote-in means a hash voted in but **never adopted**
72+
Stamping `expires_at = now + TTL` at vote-in means a hash voted in but **never adopted**
6873
(e.g. a newly voted launcher image no node ever migrated to) also expires after
6974
one TTL window. Recovery: `vote_add_launcher_hash` for an already-present entry
70-
refreshes its `last_used` (threshold vote, not unanimity).
75+
restamps its `expires_at` (threshold vote, not unanimity).
7176

7277
### Safety invariants
7378

7479
- A hash backing a **valid participant attestation is never expired**: a
7580
current participant resubmits hourly, so its valid attestation (at most
76-
`DEFAULT_EXPIRATION_DURATION_SECONDS`, currently 7 days, old) refreshed the
77-
entry's `last_used` that recently, and `last_used + TTL >= now` holds whenever
78-
`TTL >= DEFAULT_EXPIRATION_DURATION_SECONDS` — regardless of the constant's
79-
exact value. Enforced by config validation
81+
`DEFAULT_EXPIRATION_DURATION_SECONDS`, currently 7 days, old) restamped the
82+
entry's `expires_at = now + TTL` that recently, so `expires_at >= now` holds
83+
whenever `TTL >= DEFAULT_EXPIRATION_DURATION_SECONDS` — regardless of the
84+
constant's exact value. Enforced by config validation
8085
(`launcher_hash_unused_ttl_seconds >= DEFAULT_EXPIRATION_DURATION_SECONDS`);
8186
the 14-day default is `>= DEFAULT_EXPIRATION_DURATION_SECONDS` (the
8287
attestation window, currently 7 days), leaving ample margin.
@@ -99,12 +104,12 @@ sequenceDiagram
99104
participant N as Nodes
100105
101106
Ops->>C: vote_add_launcher_hash(B), threshold reached
102-
Note over C: B.last_used = now, 14-day adoption clock starts
107+
Note over C: B.expires_at = now + 14d, adoption clock starts
103108
N->>C: hourly submit_participant_info with launcher A
104-
Note over C: A.last_used refreshed hourly
109+
Note over C: A.expires_at restamped hourly (now + 14d)
105110
N->>C: hourly submit_participant_info with launcher B (after migration)
106-
Note over C: B refreshed. A freezes once the last node leaves it
107-
Note over C: A.last_used + 14d passes. A is rejected by all reads
111+
Note over C: B restamped. A freezes once the last node leaves it
112+
Note over C: A.expires_at passes. A is rejected by all reads
108113
Ops->>C: verify_tee (routine)
109114
Note over C: spawns detached self-call clean_expired_launcher_hashes
110115
C-->>C: clean_expired_launcher_hashes (private, separate receipt)
@@ -125,8 +130,10 @@ sequenceDiagram
125130
## Migration
126131

127132
New borsh field ⇒ state migration: existing entries get
128-
`last_used = migration time` — every current hash starts a fresh 14-day clock;
129-
stale testnet hashes age out with no further action.
133+
`expires_at = migration time + TTL` — every current hash starts a fresh 14-day
134+
clock; stale testnet hashes age out with no further action. The 3.13.0 migration
135+
also stamps an expiry on legacy `MockAttestation::Valid` entries (from #3785) so
136+
they become cleanable; the two steps run together.
130137

131138
## Alternatives considered
132139

0 commit comments

Comments
 (0)