Skip to content

feat(contract): operator-prepaid attestation storage (grant counter) #4015

Description

@barakeinav1

Background

Implements the funding model designed in #3972 and specified in docs/design/operator-prepaid-attestation-storage.md. An operator prepays for attestation-entry storage in a separate transaction; the node keeps self-submitting with its deposit-less function-call key. One prepayment buys one grant — permission to hold one attestation entry — and a grant returns when its entry is reclaimed. Fee is 0.02 NEAR. Nothing is refunded.

This closes the drain that is live today: submit_participant_info takes no deposit on every deployed version, so any account can create unlimited entries at roughly 1/7th of what each costs the contract, and an exhausted balance stops the contract writing state at all.

Acceptance Criteria

  • prepay_attestation_storage(account_id, grants)#[payable], permissionless, requires an attached deposit of exactly fee × grants and rejects anything else.
  • available_attestation_grants(account_id) -> u32 view. No dedicated fee view: config() already returns attestation_storage_fee_millinear, and an operator reads it once by hand (#4011 discussion).
  • available_attestation_grants: LookupMap<AccountId, u32> holds available grants; prepay +1, new entry −1, reclaimed entry +1, row deleted at zero.
  • Charging rules: a re-attestation under a TLS key the caller already owns consumes nothing; a new entry consumes one grant and is rejected if none is available; clean_invalid_attestations returns one grant to the owner of each entry it removes.
  • The precondition is evaluated read-only at the top of submit_participant_info — before any Mock verification or verify_quote round trip — and keys on ownership, not TLS-key presence. The grant is consumed at insert, so a failed attestation consumes nothing.
  • Config.attestation_storage_fee is governance-votable, defaulting to 0.02 NEAR, with the ConfigExt DTO plumbing in crates/contract/src/dto_mapping.rs.
  • State migration for the new map and Config field: frozen snapshot module plus a migrate() arm as crates/contract/src/v3_13_0_state.rs does, with regenerated borsh-schema and ABI snapshots.
  • clean_invalid_attestations_tera_gas and RESHARE_CLEAN_INVALID_ATTESTATIONS_MAX_SCAN re-validated against the added per-entry write, which is a row insert whenever the owner's row was deleted at zero.
  • Operator guide gains one step between Add the Node Account Key and Submitting Participant Info, and the stale "will incur a cost (TBD, XXX NEAR)" note citing the closed [Docs-Missing] Document cost in NEAR tokens for calling submit_participant_info #903 is removed.
  • Tests: prepay-then-submit; re-attestation consuming no grant; prepay-on-behalf by another account; rejection with no grant; a TLS key owned by another account rejected by the early check; a failed attestation consuming nothing on both sync and async paths; a reclaimed entry returning a spendable grant; the row deleted at zero; a kicked node keeping its grants; two prepayments allowing two entries; and a clean_invalid_attestations gas-budget guard for the worst case (every scanned entry removed, every owner's row absent).

#3991 - can potentially be done in same PR as well.

Resources & Additional Notes

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions