Background
The post-submit confirmation for submit_participant_info reconstructs the attestation's creation time by subtracting a hard-coded constant from the contract-stored expiry (crates/node/src/indexer/tx_sender.rs, TODO(#1637)):
storage_time = stored_expiry - DEFAULT_EXPIRATION_DURATION_SECONDS;
attestation_age = |now - storage_time|;
is_fresh = attestation_age < MAX_ATTESTATION_AGE; // 120s
But stored_expiry is stamped by whoever verifies and stores the attestation — the contract (AcceptedAttestation::dstack, crates/mpc-attestation/src/attestation.rs:71) — using the contract's compiled-in DEFAULT_EXPIRATION_DURATION_SECONDS. The node then subtracts the node's constant. When the two differ, the reconstructed age is off by the difference.
This is live now: contract runs 3.12 (DEFAULT_EXPIRATION_DURATION_SECONDS = 7 days), nodes run 3.13 (changed to 1 day in #3626). Reconstructed age = 7d − 1d = 6 days, far over the 120s MAX_ATTESTATION_AGE, so the check never returns Executed:
tx_sender: node found dstack attestation on chain attestation_age=518390.57s attestation_is_fresh=false
ERROR periodic_attestation_submission: failed to submit attestation cause=attestation submission was not executed backoff_duration=60s
Each submission lands on-chain successfully, but the node treats it as NotExecuted and retries at the 60s backoff ceiling (+10s observe) — a ~70s submit_participant_info loop per node, indefinitely, each tx carrying a ~60 KB quote+collateral payload.
Acceptance Criteria
- Attestation submission confirmation does not depend on node and contract sharing the same
DEFAULT_EXPIRATION_DURATION_SECONDS (confirm via stored-vs-submitted identity, or an explicit creation timestamp, rather than expiry − constant).
- A node running a different version from the contract does not enter a permanent
submit_participant_info resubmission loop.
Related: #1637, #1639 (extract timestamp from the certificate instead of the now ± constant heuristic).
Background
The post-submit confirmation for
submit_participant_inforeconstructs the attestation's creation time by subtracting a hard-coded constant from the contract-stored expiry (crates/node/src/indexer/tx_sender.rs,TODO(#1637)):But
stored_expiryis stamped by whoever verifies and stores the attestation — the contract (AcceptedAttestation::dstack,crates/mpc-attestation/src/attestation.rs:71) — using the contract's compiled-inDEFAULT_EXPIRATION_DURATION_SECONDS. The node then subtracts the node's constant. When the two differ, the reconstructed age is off by the difference.This is live now: contract runs 3.12 (
DEFAULT_EXPIRATION_DURATION_SECONDS = 7 days), nodes run 3.13 (changed to1 dayin #3626). Reconstructed age =7d − 1d = 6 days, far over the 120sMAX_ATTESTATION_AGE, so the check never returnsExecuted:Each submission lands on-chain successfully, but the node treats it as
NotExecutedand retries at the 60s backoff ceiling (+10s observe) — a ~70ssubmit_participant_infoloop per node, indefinitely, each tx carrying a ~60 KB quote+collateral payload.Acceptance Criteria
DEFAULT_EXPIRATION_DURATION_SECONDS(confirm via stored-vs-submitted identity, or an explicit creation timestamp, rather thanexpiry − constant).submit_participant_inforesubmission loop.Related: #1637, #1639 (extract timestamp from the certificate instead of the
now ± constantheuristic).