Skip to content

Commit 9ae5881

Browse files
barakeinav1gilcu3
andauthored
feat(contract): operator-prepaid attestation storage (grant counter) (#4016)
Co-authored-by: Reynaldo Gil Pons <gilcu3@gmail.com>
1 parent 3ae3641 commit 9ae5881

27 files changed

Lines changed: 1034 additions & 94 deletions

crates/contract/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ stateDiagram-v2
264264
| `verify_foreign_transaction(request: VerifyForeignTransactionRequestArgs)` | Submits a foreign-chain transaction verification request to the contract. Requires a deposit of 1 yoctonear and that the requested foreign chain is in the contract's supported set. Duplicate submissions of the same request (same caller, domain, chain, and payload) while an earlier one is still pending are queued and all receive the same response when the MPC nodes reply; the queue is bounded — concurrent duplicates beyond that bound are rejected with `PendingRequestQueueFull`. | deferred to promise | `10 Tgas` | `~7 Tgas` |
265265
| `public_key(domain: Option<DomainId>)` | Read-only function; returns the public key used for the given domain (defaulting to first). | `Result<PublicKey, Error>` | | |
266266
| `derived_public_key(path: String, predecessor: Option<AccountId>, domain: Option<DomainId>)` | Generates a derived public key for a given path and account, for the given domain (defaulting to first). | `Result<PublicKey, Error>` | | |
267+
| `prepay_attestation_storage(account_id: AccountId, grants: u32)` | Prepays attestation-entry storage for `account_id`. One grant permits one stored attestation. Payable and permissionless — anyone may prepay for any account, which is how an operator funds a node whose function-call access key cannot attach a deposit. Requires an attached deposit of exactly `attestation_storage_fee_millinear × grants` (see `config`) and rejects anything else. Nothing is refunded and there is no withdrawal. | `Result<(), Error>` | 30Tgas | ~4Tgas |
268+
| `available_attestation_grants(account_id: AccountId)` | Read-only function; returns the grants `account_id` has available — bought, minus those currently backing a stored attestation. `0` therefore means either "never prepaid" or "prepaid, and the grant is backing an entry"; cross-reference `get_tee_accounts` to distinguish them. | `u32` | | |
267269

268270
#### SignRequestArgs (Latest version)
269271

@@ -310,7 +312,7 @@ These functions require the caller to be a participant or candidate.
310312
| `vote_cancel_keygen(next_domain_id: u64)` | For Initializing state only. Votes to cancel the key generation (identified by the next_domain_id) and revert to the Running state. | `Result<(), Error>` | TBD | TBD |
311313
| `propose_update(args: ProposeUpdateArgs)` | Proposes an update to the contract, requiring an attached deposit. | `Result<UpdateId, Error>` | TBD | TBD |
312314
| `vote_update(id: UpdateId)` | Votes on a proposed update. If the threshold is met, the update is executed. | `Result<bool, Error>` | TBD | TBD |
313-
| `submit_participant_info(proposed_participant_attestation: Attestation, tls_public_key: Ed25519PublicKey)` | Submits the tee participant info for a potential candidate. c.f. TEE section | `Result<(), Error>` | TBD | TBD |
315+
| `submit_participant_info(proposed_participant_attestation: Attestation, tls_public_key: Ed25519PublicKey)` | Submits the tee participant info for a potential candidate. c.f. TEE section. Storing a new attestation entry consumes one attestation-storage grant, so the account must have one prepaid via `prepay_attestation_storage`; re-submitting for a TLS key the account already owns does not. | `Result<(), Error>` | TBD | TBD |
314316

315317
### Developer API
316318

crates/contract/src/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ const DEFAULT_RESOLVE_VERIFICATION_TERA_GAS: u64 = 60;
4444
/// Default TTL after which a launcher image hash unused by any participant is evicted.
4545
pub(crate) const DEFAULT_LAUNCHER_HASH_UNUSED_TTL_SECONDS: u64 = 14 * 24 * 60 * 60; // 14 days
4646

47+
/// One attestation-storage grant, in milliNEAR.
48+
const DEFAULT_ATTESTATION_STORAGE_FEE_MILLINEAR: u64 = 20;
49+
4750
/// Config for V2 of the contract.
4851
#[near(serializers=[borsh, json])]
4952
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -87,6 +90,8 @@ pub(crate) struct Config {
8790
/// Applied when an entry's expiry is next stamped (vote-in, re-vote, or a refresh on
8891
/// use), not retroactively — changing it does not re-date existing entries.
8992
pub(crate) launcher_hash_unused_ttl_seconds: u64,
93+
/// Fee, in milliNEAR, charged for one attestation-storage grant.
94+
pub(crate) attestation_storage_fee_millinear: u64,
9095
}
9196

9297
impl Default for Config {
@@ -117,6 +122,7 @@ impl Default for Config {
117122
verifier_tera_gas: DEFAULT_VERIFIER_TERA_GAS,
118123
resolve_verification_tera_gas: DEFAULT_RESOLVE_VERIFICATION_TERA_GAS,
119124
launcher_hash_unused_ttl_seconds: DEFAULT_LAUNCHER_HASH_UNUSED_TTL_SECONDS,
125+
attestation_storage_fee_millinear: DEFAULT_ATTESTATION_STORAGE_FEE_MILLINEAR,
120126
}
121127
}
122128
}

crates/contract/src/dto_mapping.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,9 @@ impl TryFrom<near_mpc_contract_interface::types::InitConfig> for Config {
505505
if let Some(v) = config_ext.resolve_verification_tera_gas {
506506
config.resolve_verification_tera_gas = v;
507507
}
508+
if let Some(v) = config_ext.attestation_storage_fee_millinear {
509+
config.attestation_storage_fee_millinear = v;
510+
}
508511
if let Some(v) = config_ext.launcher_hash_unused_ttl_seconds {
509512
config.launcher_hash_unused_ttl_seconds = v;
510513
}
@@ -545,6 +548,7 @@ impl From<&Config> for near_mpc_contract_interface::types::Config {
545548
.remove_non_participant_tee_verifier_votes_tera_gas,
546549
verifier_tera_gas: value.verifier_tera_gas,
547550
resolve_verification_tera_gas: value.resolve_verification_tera_gas,
551+
attestation_storage_fee_millinear: value.attestation_storage_fee_millinear,
548552
launcher_hash_unused_ttl_seconds: value.launcher_hash_unused_ttl_seconds,
549553
}
550554
}
@@ -579,6 +583,7 @@ impl TryFrom<near_mpc_contract_interface::types::Config> for Config {
579583
.remove_non_participant_tee_verifier_votes_tera_gas,
580584
verifier_tera_gas: value.verifier_tera_gas,
581585
resolve_verification_tera_gas: value.resolve_verification_tera_gas,
586+
attestation_storage_fee_millinear: value.attestation_storage_fee_millinear,
582587
launcher_hash_unused_ttl_seconds: value.launcher_hash_unused_ttl_seconds,
583588
};
584589

crates/contract/src/errors.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,14 @@ pub enum InvalidParameters {
129129
MalformedPayload { reason: String },
130130
#[error("Attached deposit is lower than required. Attached: {attached}, required: {required}")]
131131
InsufficientDeposit { attached: u128, required: u128 },
132+
#[error(
133+
"attached deposit {attached} must be exactly the attestation storage fee times the requested grants, {required}"
134+
)]
135+
UnexpectedDeposit { attached: u128, required: u128 },
136+
#[error(
137+
"no attestation storage grant available for {account_id}; prepay one with prepay_attestation_storage"
138+
)]
139+
NoAttestationStorageGrant { account_id: String },
132140
#[error("Provided gas is lower than required. Provided: {provided}, required: {required}")]
133141
InsufficientGas { provided: u64, required: u64 },
134142
#[error("This sign request has timed out, was completed, or never existed.")]

0 commit comments

Comments
 (0)