Skip to content

fix(contract): make mock attestations cleanable via expiry - #3785

Merged
barakeinav1 merged 15 commits into
mainfrom
fix/3293-mock-attestation-cleanup
Jul 29, 2026
Merged

fix(contract): make mock attestations cleanable via expiry#3785
barakeinav1 merged 15 commits into
mainfrom
fix/3293-mock-attestation-cleanup

Conversation

@barakeinav1

@barakeinav1 barakeinav1 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Closes #3293

Implements the expiry-based approach from the issue thread: give mock attestations the same expiry window real (Dstack) ones already get, so the existing clean_invalid_attestations flow can evict them.

Contract

  • AcceptedAttestation::mock now stamps DEFAULT_EXPIRATION_DURATION_SECONDS on every accepted mock, converting a bare MockAttestation::Valid into an otherwise-unconstrained expiring WithConstraints (an explicit caller-set expiry is preserved). No borsh-layout change — WithConstraints already exists.
  • Existing stored mock entries are migrated during upgrade (stamp_expiry_on_legacy_mocks), so the stale Mock::Valid entries already on-chain also become cleanable.

Node (upgrade-flow compatibility)

Assumes the node is upgraded before the contract, so the upgraded node must work against both the old and new contract.

The node confirmed mock submissions landed by identity (stored == submitted). Once the contract stores a submitted Mock::Valid as WithConstraints{expiry}, identity never matches, so a node on the new contract would treat every mock submit as not-landed and retry until it errored (and my expiry change also makes mocks expire, which triggers resubmission). Fixed:

  • submitted_attestation_landed: confirm an expiry-carrying mock via the same expiry-changed heuristic already used for Dstack; fall back to identity for the bare Valid form stored by older contracts.
  • read_stored_attestation_expiry (renamed from read_stored_dstack_expiry): also return the mock expiry so the pre-submit baseline is accurate.
  • Added VerifiedAttestation/MockAttestation::expiry_timestamp_seconds helpers on the shared dtos.

Compatibility matrix (new node):

  • old contract → stored Mock::Valid (no expiry) → identity match → landed ✓
  • new contract → stored WithConstraints{expiry} → expiry-changed heuristic → landed ✓

Tests

  • mpc-attestation: with_expiry__should_* (Valid→expiring constraints, fill missing expiry + keep other constraints, preserve explicit expiry, leave Invalid unchanged); updated valid_mock_attestation_succeeds_verification for the new stored shape
  • contract: clean_invalid_attestations__should_remove_accepted_mock_valid_after_expiry, stamp_expiry_on_legacy_mocks__should_make_valid_mock_cleanable
  • node: submitted_attestation_landed__should_confirm_mock_with_changed_expiry, ..._should_reject_mock_with_unchanged_expiry (old-contract identity path still covered by the existing mock tests)

Out of scope / open question

The genesis sentinel path (with_mocked_participant_attestations, used by init/init_running) still stores non-expiring Valid mocks. The migration converts existing ones, but fresh inits create new non-expiring entries. Stamping those too would change the #1087 placeholder semantics (participants would auto-expire if they never submit a real attestation), so I left it out of this PR.

The DstackAttestation example references Quote, Collateral, TcbInfo, and
ExpectedMeasurements without imports, breaking the --all-features doctest.
Mark the block rust,ignore since it only illustrates the struct shape.
MockAttestation::Valid passed re-verification unconditionally, so its
stored entry could never be evicted by clean_invalid_attestations and
lingered in get_tee_accounts after a key migration (#3293).

- Stamp DEFAULT_EXPIRATION_DURATION_SECONDS on every accepted mock, as
  Dstack attestations already do, converting a bare Valid into an
  expiring WithConstraints (explicit expiries are preserved).
- Migrate existing stored mock entries during upgrade so pre-existing
  stale Valid entries also become cleanable.
With the contract now stamping an expiry on accepted mocks, a submitted
Mock::Valid is stored as WithConstraints{expiry}. The node confirmed mock
submissions by identity (stored == submitted), which would never match the
re-stamped form, so a node on the new contract would treat every mock
submit as not-landed and retry until it errored.

Assuming node-is-upgraded-before-contract, the upgraded node must work
with both contract versions:
- submitted_attestation_landed: confirm an expiry-carrying mock via the
  same expiry-changed heuristic used for Dstack; fall back to identity for
  the bare Valid form stored by older contracts.
- read_stored_attestation_expiry (renamed from _dstack): also return the
  mock expiry so the pre-submit baseline is accurate.
- add VerifiedAttestation/MockAttestation::expiry_timestamp_seconds helpers.
…y tests

- Use get_mut in stamp_expiry_on_legacy_mocks instead of clone-and-reinsert;
  drop the now-unneeded Clone derive on NodeAttestation.
- Add unit tests for MockAttestation::with_expiry (public API).
- Use intra-doc links in the new doc comments per engineering standards.
Resolve migration conflict in v3_13_0_state.rs (keep both main's
OldConfig->Config conversion and the legacy-mock expiry stamping), adapt
the new test to main's verify_and_store_mock API, and apply review fixups:
- drop #3293 references from code comments (kept the explanations)
- use TeeState::current_time_seconds() in the migration instead of an
  inline block_timestamp_ms()/1000
- add TODO(#3978) marking stamp_expiry_on_legacy_mocks as transitional
- shorten the AcceptedAttestation::mock doc comment
@barakeinav1
barakeinav1 marked this pull request as ready for review July 28, 2026 06:34
Copilot AI review requested due to automatic review settings July 28, 2026 06:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes mock attestations eligible for cleanup by stamping them with an expiry (matching the existing Dstack expiry window), migrates already-stored legacy MockAttestation::Valid entries during contract upgrade, and updates the node + shared DTOs so an upgraded node can confirm submissions against both old (identity-stored mocks) and new (expiry-stamped mocks) contract behavior.

Changes:

  • Stamp DEFAULT_EXPIRATION_DURATION_SECONDS expiry onto accepted mock attestations (preserving any explicit caller-provided expiry) and add a migration to stamp expiry onto legacy stored mocks.
  • Update node confirmation logic to treat expiry-stamped mocks like Dstack (expiry-change heuristic), while retaining identity fallback for old-contract / genesis-sentinel Mock::Valid.
  • Add helpers/tests across mpc-attestation, contract, and node to cover the new expiry-based behavior.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
crates/node/src/tee/remote_attestation.rs Switches node baseline read to the generalized attestation-expiry reader.
crates/node/src/indexer/tx_sender.rs Extends “landed” confirmation to expiry-stamped mocks; simplifies stored-expiry extraction; adds tests.
crates/node/src/indexer/fake.rs Updates fake expiry reader method name to match renamed trait API.
crates/node/src/indexer.rs Renames/expands expiry reader trait to cover both Dstack and expiry-carrying mocks.
crates/near-mpc-contract-interface/src/types/attestation.rs Adds expiry_timestamp_seconds() helpers on shared DTOs (VerifiedAttestation, MockAttestation).
crates/mpc-attestation/tests/test_attestation_verification.rs Updates verification test to assert mock acceptance produces WithConstraints{expiry}.
crates/mpc-attestation/src/attestation.rs Implements mock expiry stamping (AcceptedAttestation::mock + MockAttestation::with_expiry) and unit tests.
crates/contract/src/v3_13_0_state.rs Runs one-time migration during upgrade to stamp expiry on legacy stored mock attestations.
crates/contract/src/tee/tee_state.rs Adds migration helper stamp_expiry_on_legacy_mocks and tests proving mocks become cleanable after expiry.
crates/contract/README.md Marks a Rust doc snippet as rust,ignore to avoid doctest compilation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/node/src/indexer/tx_sender.rs
Comment thread crates/near-mpc-contract-interface/src/types/attestation.rs Outdated
Comment thread crates/node/src/indexer.rs Outdated
@claude

claude Bot commented Jul 28, 2026

Copy link
Copy Markdown

Pull request overview

Makes mock attestations cleanable by stamping them with the same DEFAULT_EXPIRATION_DURATION_SECONDS window used by real (Dstack) attestations, so the existing clean_invalid_attestations sweep can evict stale mocks. A one-time migration (stamp_expiry_on_legacy_mocks) rewrites already-stored Mock::Valid entries as expiring WithConstraints. The node side is updated to (a) confirm mock submissions via the expiry-changed heuristic when stored mocks carry an expiry, falling back to identity match for older contract versions, and (b) surface the mock expiry via the renamed read_stored_attestation_expiry.

Changes:

  • New MockAttestation::with_expiry(...) helper: converts bare Valid → expiring WithConstraints, preserves caller-set expiry, leaves Invalid alone.
  • AcceptedAttestation::mock now stamps the default expiry, mirroring dstack.
  • Contract migration (v3_13_0_state) stamps expiries on all stored mocks during upgrade.
  • Node: renamed reader + submitted_attestation_landed uses expiry heuristic for mocks with expiry; identity fallback for legacy Mock::Valid.
  • New expiry_timestamp_seconds() helpers on VerifiedAttestation and MockAttestation DTOs.
  • Tests for the four with_expiry cases, the accepted-mock and legacy-mock cleanup paths, and the node's expiry/identity landed check.

Reviewed changes

Per-file summary
File Description
crates/contract/README.md Marks Rust snippet with ,ignore so it doesn't compile.
crates/contract/src/tee/tee_state.rs Adds stamp_expiry_on_legacy_mocks migration helper; makes current_time_seconds pub(crate); adds two tests.
crates/contract/src/v3_13_0_state.rs Invokes stamp_expiry_on_legacy_mocks during the 3.13.0 → current migration.
crates/mpc-attestation/src/attestation.rs Adds MockAttestation::with_expiry; AcceptedAttestation::mock now stamps expiry; four unit tests.
crates/mpc-attestation/tests/test_attestation_verification.rs Updates verification test to expect the new stored shape (WithConstraints w/ expiry).
crates/near-mpc-contract-interface/src/types/attestation.rs Adds expiry_timestamp_seconds() on VerifiedAttestation and MockAttestation DTOs.
crates/node/src/indexer.rs Renames trait method to read_stored_attestation_expiry, returns mock expiry too.
crates/node/src/indexer/fake.rs Rename plumbed through the fake reader.
crates/node/src/indexer/tx_sender.rs submitted_attestation_landed uses expiry heuristic for mocks that carry one; identity fallback for legacy; new tests.
crates/node/src/tee/remote_attestation.rs Rename plumbed through periodic and removal-monitor call sites and their stubs.

Findings

Non-blocking (nits, follow-ups, suggestions):

  • crates/mpc-attestation/src/attestation.rs:152with_expiry preserves an explicit caller-set expiry, but that opens a corner: a caller who repeatedly submits Mock::WithConstraints { expiry: Some(T), .. } will see the stored expiry never change on re-submit, so submitted_attestation_landed will keep reporting NotExecuted and the node will loop. Real nodes submit Mock::Valid or Dstack in prod so this is caller misuse rather than a live bug, but worth a docstring note on with_expiry (or a debug-log in the node when pre/post expiries match on a mock with Some(T) matching the submitted attestation).

  • crates/contract/src/tee/tee_state.rs:488 — raw + on u64 seconds. AcceptedAttestation::dstack already uses the same pattern so this is not new, and overflow is unreachable in practice, but per docs/engineering-standards.md §"Use safe arithmetic methods" this is a candidate for checked_add(...).expect("timestamp overflow") (contracts are allowed to panic). Same call at crates/mpc-attestation/src/attestation.rs:92.

  • crates/contract/src/tee/tee_state.rs:503-510 — inside the loop, if let VerifiedAttestation::Mock(mock) = ... is redundant since the key set was already filtered to Mock, and get_mut cannot miss because &mut self is held. Consider simplifying with expect("filtered above") on the get_mut and pattern binding the Mock variant directly — the current defensive form makes the reader wonder what invariant they're guarding.

  • crates/contract/src/v3_13_0_state.rs:112-116 — the migration will stamp expiries on the genesis-sentinel mocks written by with_mocked_participant_attestations too, which the PR description explicitly says was intended to be out of scope. That is consistent with Follow-up (#3293): stamp expiry on genesis mock sentinels and drop node mock-identity fallback #3786's "stamp expiry on genesis mock sentinels" scope, but the doc on stamp_expiry_on_legacy_mocks currently reads as though it only targets user-submitted Mock::Valid — one line clarifying that all stored mocks (including sentinels) are stamped would prevent surprise later.

  • crates/contract/src/tee/tee_state.rs:797-838stamp_expiry_on_legacy_mocks__should_make_valid_mock_cleanable sets block_timestamp to (DEFAULT + 1) * 1e9 twice with no intervening advance. The comment says "the clock later advances past that stamped window" but the second set_block_timestamp is a no-op. Consider dropping the second call or setting a distinct later timestamp so the test reads as intended.

  • crates/mpc-attestation/src/attestation.rs:87-90 — docstring uses plain backticks (`Valid`, `Mock`) rather than intra-doc links per docs/engineering-standards.md §"Use rustdoc intra-doc links". Applies to the surrounding block; the newer paragraphs already use [MockAttestation::with_expiry], so this is easy to align.

  • crates/contract/src/tee/tee_state.rs:490// Collect keys before mutating to avoid iterator invalidation. is borderline paraphrase-of-code (the reader can see the .collect() two lines below). Either drop it or make it a WHY note pointing at the borrow-checker constraint on IterableMap.

  • crates/node/src/indexer/tx_sender.rs:215-217TODO(#3786) correctly references an existing follow-up (verified). Nit: mention that once genesis sentinels are stamped (also tracked by Follow-up (#3293): stamp expiry on genesis mock sentinels and drop node mock-identity fallback #3786), the whole identity branch can be deleted, not just gated — the current wording says "drop this identity fallback" which is right, worth keeping.

✅ Approved

Overall: the design is clean (single with_expiry helper does all the shape logic), the upgrade-flow reasoning in the PR body matches the code, and the tests cover both the domain change and the migration path. Findings above are all non-blocking.

Previously with_expiry preserved a caller-supplied mock expiry, so a
submitter could set an arbitrarily long (uncleanable) expiry via
MockAttestation::WithConstraints. The contract now owns the maximum mock
lifetime — capping the expiry at now + DEFAULT_EXPIRATION_DURATION_SECONDS
(a shorter caller value is kept) — matching the Dstack path where the
contract sets the expiry outright. The migration likewise caps oversized
legacy entries.
@claude

claude Bot commented Jul 28, 2026

Copy link
Copy Markdown

PR title type suggestion: This PR adds new functionality (expiry mechanism for mock attestations), so the type prefix should probably be feat: instead of fix:.
Suggested title: feat(contract): make mock attestations cleanable via expiry

- Clarify that a mock with no expiry can be an older contract OR a genesis
  sentinel (VerifiedAttestation dto + node ReadAttestationExpiry trait doc).
- Note stamp_expiry_on_legacy_mocks stamps all stored mocks incl. genesis
  sentinels.
- Use an intra-doc link for MockAttestation::Valid in AcceptedAttestation::mock.
- Drop a redundant set_block_timestamp in the legacy-mock cleanup test.
@claude

claude Bot commented Jul 28, 2026

Copy link
Copy Markdown

PR title type suggestion: This PR adds a new capability to make mock attestations cleanable via expiry. Since it's adding new functionality rather than fixing a bug, the type prefix should be feat: instead of fix:.

Suggested title: feat(contract): make mock attestations cleanable via expiry

@barakeinav1

Copy link
Copy Markdown
Contributor Author

Thanks — dispositions per finding (addressed in a874994):

  • with_expiry preserves caller expiry → resubmit loop — now stale: with_expiry was changed to cap (not preserve) the expiry, so the contract owns the max lifetime. The residual corner only affects a caller resubmitting a fixed WithConstraints expiry (caller misuse); prod submits Mock::Valid, whose stamped expiry advances each resubmit and always confirms. No change.
  • raw +checked_add — declined: matches the sibling AcceptedAttestation::dstack, overflow is unreachable (timestamp + 1 day), and it's engineering-standards exception Triple generation with mock networking #3. Changing only these would diverge from dstack.
  • redundant if let Mockexpect(...) — declined: the defensive if let is exactly what "Maintain Local Reasonability" prescribes; expect() would add a panic that depends on the distant .filter(), which that section warns against.
  • migration doc / genesis sentinels — fixed: doc now says it stamps all stored mocks, incl. genesis sentinels.
  • double set_block_timestamp in the legacy-mock test — fixed: dropped the redundant second call, comment corrected.
  • fn mock docstring intra-doc link — fixed: Valid is now [MockAttestation::Valid].
  • "collect keys" comment — kept: it's a genuine why (can't mutate IterableMap while iterating).
  • TODO(#3786) wording — kept as-is (verified correct).

@barakeinav1

Copy link
Copy Markdown
Contributor Author

Keeping fix: this resolves a reported defect (#3293 — stored mock entries could never be cleaned). feat is defensible, but fix matches the bug it closes.

These tests submitted mocks with Some(u64::MAX) expiries and asserted
get_attestation returned them verbatim. Now that the contract caps a
mock's expiry at now + DEFAULT_EXPIRATION_DURATION_SECONDS, use in-window
expiries (computed from the current block time) so they are preserved and
stay distinct.

@haiyuechen-nearone haiyuechen-nearone left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the general flow LGTM, left a question about the comments in crates/node/src/indexer/tx_sender.rs

Comment thread crates/node/src/indexer/tx_sender.rs Outdated
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

PR title type suggestion: The phrasing 'make mock attestations cleanable' suggests this is adding new functionality rather than fixing a bug. Consider using feat: instead of fix:.

Suggested title: feat(contract): make mock attestations cleanable via expiry

@netrome netrome left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change looks correct, but it feels hacky to overwrite expiry times on mock attestations on the verify call. That alone is not a hard blocker though, but the migration helper should be moved and the comment on submitted_attestation_landed should be simplified so it's not confusing people navigating the code base.

Comment thread crates/node/src/indexer/tx_sender.rs Outdated
Comment thread crates/node/src/indexer/tx_sender.rs Outdated
Comment thread crates/contract/src/tee/tee_state.rs Outdated
Comment on lines +91 to 102
fn mock(mock_attestation: &MockAttestation, current_timestamp_seconds: u64) -> Self {
let expiry_timestamp_seconds =
current_timestamp_seconds + DEFAULT_EXPIRATION_DURATION_SECONDS;
Self {
attestation: VerifiedAttestation::Mock(mock_attestation.clone()),
attestation: VerifiedAttestation::Mock(
mock_attestation
.clone()
.with_expiry(expiry_timestamp_seconds),
),
advisory_ids: Vec::new(),
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm definitely not a fan of overwriting the inner expiry timestamp on the verify call. This feels like it opens the door to bugs and risks, but I see you raised #4005 to tackle this so I won't consider it a hard blocker.

@barakeinav1 barakeinav1 Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the location of this is wrong.
After verifying an attestation, we store it. In this case we cap the expiry at our default now + DEFAULT_EXPIRATION_DURATION_SECONDS (if the submitted one was larger). This is deliberate: it stops a submitter from storing an arbitrarily long, uncleanable expiry (the goal of this PR).
For dstack we always set now + DEFAULT_EXPIRATION_DURATION_SECONDS, since extracting the actual expiry from the cert chain was hard (#1639).

#4005 is meant to align both dstack/mock to the same logic.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right I see the .with_expiry method performs a min between the existing expiry and the cap. That's a bit confusing though. I'd expect .with_expiry to be a plain setter. Perhaps worth renaming it to .cap_expiry() or something similar?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — renamed with_expirywith_expiry_capped_at in 1e4b663 (method + both call sites + tests), so it no longer reads as a pure setter. If #4005 later makes it a true override (contract owns the expiry outright, Dstack-style), it'd go back to a plain with_expiry.

- Move stamp_expiry_on_legacy_mocks (+ its test) into the v3_13_0_state
  migration module, since it is migration-only (netrome).
- Simplify submitted_attestation_landed docstring: a changed stored expiry
  is enough to conclude the submit landed; drop the confusing 'identity'
  wording (the legacy equality fallback is covered by TODO(#3786)).
- Restore the TODO(#3978) on the moved migration helper (it was dropped
  when the helper moved to v3_13_0_state).
- The simplified landing docstring referenced `TODO(#3786)` in prose, which
  the todo-format check flags as a malformed TODO; reword to drop it (the
  inline fallback TODO is self-sufficient).
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown

PR title type suggestion: This PR adds a new capability to clean mock attestations via expiry, which is a feature addition rather than a bug fix. The type prefix should probably be feat: instead of fix:.

Suggested title: feat(contract): make mock attestations cleanable via expiry

netrome
netrome previously approved these changes Jul 29, 2026

@netrome netrome left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating 🙏

It caps (min of existing and provided), not sets, so the old name read as
a pure setter and misled readers (netrome). Renamed the method, its two
call sites (AcceptedAttestation::mock, the migration helper), and its tests.
@barakeinav1
barakeinav1 enabled auto-merge July 29, 2026 15:44

@netrome netrome left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for renaming 🙏

@barakeinav1
barakeinav1 added this pull request to the merge queue Jul 29, 2026
Merged via the queue into main with commit b4bea09 Jul 29, 2026
15 checks passed
@barakeinav1
barakeinav1 deleted the fix/3293-mock-attestation-cleanup branch July 29, 2026 17:36
barakeinav1 added a commit that referenced this pull request Jul 30, 2026
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).
barakeinav1 added a commit that referenced this pull request Jul 30, 2026
…ependency

Two facts from the full bot review that the earlier summary did not surface:

- #3785 is merged (2026-07-29), so "land it first or alongside" described a
  dependency that no longer exists. Replaced with what actually remains: mock
  entries are sweepable now, but TeeState::with_mocked_participant_attestations
  still stores bare non-expiring Mock::Valid sentinels at init, which never fail
  re-verification and so are never swept or granted.

- The legacy grandfather does not work the way we assumed when deciding to accept
  it. A live node re-attests under rule 1, so its entry never fails
  re-verification, is never swept, and yields no grant -- operators currently
  running nodes get nothing and need nothing. Only *abandoned* entries convert to
  grants. The number is still negligible today (14 mainnet, 31 testnet, nearly all
  live) and the deploy-time count check still bounds it, but the rationale is
  "abandoned entries are rare", not "it rewards our existing operators".

Also: say why prepay keeps the remainder instead of following the contract's
require_deposit + refund_to convention, and note that making the fee votable needs
the ConfigExt DTO plumbing and a borsh-schema snapshot, not just the migration.
barakeinav1 added a commit that referenced this pull request Jul 30, 2026
- Status line removed; the design is settled, not a draft for review.
- Testing removed outright: the test plan belongs in the implementation PR.
- Implementation notes removed, but two of its items were costs of design
  decisions rather than implementation chores, so they moved next to the decisions
  that cause them instead of disappearing: rule 3 now carries its own sweep
  gas-budget caveat, and the votable-Config row in Decisions carries the state
  migration, ConfigExt plumbing and snapshot regeneration. The non-universal
  sweepability caveat moved to the Security residual about swept entries, which is
  what it qualifies.

Dropped entirely: the #3785 dependency note (merged, so there is nothing to
sequence) and the runbook-coordination reminder (Operator UX already says
operators must prepay first).

Also repaired the API table, which an earlier edit had split by inserting a
paragraph between its rows.

259 -> 144 lines across this and the previous trim.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MockAttestation::Valid entries cannot be cleaned from contract storage

4 participants