Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d0579af
wip
DSharifi Dec 10, 2025
fa86044
extended mpc-attestation to return validations
DSharifi Dec 10, 2025
695ee21
add tests for re-verification
DSharifi Dec 10, 2025
6358418
wip
DSharifi Dec 11, 2025
0e46166
code ocmpiles
DSharifi Dec 12, 2025
c694b84
add test case for re-insertions
DSharifi Dec 12, 2025
8321e49
wip
DSharifi Dec 12, 2025
6ad6e5a
finish mpc-attestation
DSharifi Dec 15, 2025
39489d5
test: added unit testst to verify attestation fails
DSharifi Dec 15, 2025
986c431
update field in DTO to creation timestamp
DSharifi Dec 15, 2025
42a4524
Merge remote-tracking branch 'origin/main' into 1629-contract-should-…
DSharifi Dec 15, 2025
aec3a44
fix dto mapping
DSharifi Dec 15, 2025
c3ce22a
fix dto mapping
DSharifi Dec 15, 2025
c20900d
fix node mapping
DSharifi Dec 15, 2025
8781ce0
merge fix for attestation tests
DSharifi Dec 15, 2025
b86a281
add unit test coverage
DSharifi Dec 15, 2025
a4d14f1
update integration test
DSharifi Dec 15, 2025
177e0c1
update sandbox tests
DSharifi Dec 15, 2025
a6c7a01
cargo fmt
DSharifi Dec 15, 2025
fba0bc7
update abi snapshot
DSharifi Dec 15, 2025
602918a
remove comments
DSharifi Dec 15, 2025
80d1162
revert nearcore to origin main
DSharifi Dec 15, 2025
d80d86f
cargo shear fix remove rstest
DSharifi Dec 15, 2025
59a028c
Merge remote-tracking branch 'origin/main' into 1629-contract-should-…
DSharifi Dec 15, 2025
3eb4d5c
.
DSharifi Dec 15, 2025
ca1cd2b
wip v302 state
DSharifi Dec 15, 2025
82036b5
.
DSharifi Dec 15, 2025
a918aff
Merge branch '1629-contract-should-not-store-full-attestation-submiss…
DSharifi Dec 15, 2025
673963b
wip
DSharifi Dec 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/contract-interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pub mod types {
pub use attestation::{
AppCompose, Attestation, Collateral, DstackAttestation, EventLog, MockAttestation, TcbInfo,
VerifiedAttestation, VerifiedDstackAttestation,
};
pub use config::{Config, InitConfig};
pub use crypto::{
Expand Down
52 changes: 50 additions & 2 deletions crates/contract-interface/src/types/attestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,54 @@ pub enum Attestation {
Mock(MockAttestation),
}

#[derive(
Clone,
Debug,
Eq,
PartialEq,
Ord,
PartialOrd,
Hash,
Serialize,
Deserialize,
BorshSerialize,
BorshDeserialize,
)]
#[cfg_attr(
all(feature = "abi", not(target_arch = "wasm32")),
derive(schemars::JsonSchema)
)]
pub enum VerifiedAttestation {
Dtack(VerifiedDstackAttestation),
Mock(MockAttestation),
}

#[derive(
Clone,
Debug,
Eq,
PartialEq,
Ord,
PartialOrd,
Hash,
Serialize,
Deserialize,
BorshSerialize,
BorshDeserialize,
)]
#[cfg_attr(
all(feature = "abi", not(target_arch = "wasm32")),
derive(schemars::JsonSchema)
)]
pub struct VerifiedDstackAttestation {
/// The digest of the MPC image running.
pub mpc_image_hash: [u8; 32],
/// The digest of the MPC image running.
pub launcher_compose_hash: [u8; 32],
/// Unix timestamp for when the attestation was created.
pub creation_time_stamp_seonds: u64,
Copy link
Contributor

Choose a reason for hiding this comment

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

where do we get the time from?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The comment is s currently misleading. The timestamp is taken from "now" time argument during the initial verification, so it's the timestamp of when the contract first saw the attestation and not the creation date.

In a follow up PR we should refactor this internal implementation to use the timestamp embedded in the attestation instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pub creation_time_stamp_seonds: u64,
pub creation_time_stamp_seconds: u64,

you will need to change in 5 places

}

#[derive(
Clone,
Eq,
Expand Down Expand Up @@ -78,8 +126,8 @@ pub enum MockAttestation {
WithConstraints {
mpc_docker_image_hash: Option<Sha256Digest>,
launcher_docker_compose_hash: Option<Sha256Digest>,
/// Unix time stamp for when this attestation expires.
expiry_time_stamp_seconds: Option<u64>,
/// Unix time stamp for when this attestation was created.
creation_time_stamp_seconds: Option<u64>,
},
}

Expand Down
10 changes: 10 additions & 0 deletions crates/contract-interface/src/types/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ pub struct InitConfig {
pub cleanup_orphaned_node_migrations_tera_gas: Option<u64>,
/// Prepaid gas for a `remove_non_participant_update_votes` call.
pub remove_non_participant_update_votes_tera_gas: Option<u64>,
/// Contract defined time to live for node attestations. Participants
/// must refresh their attestations before `attestation_max_validity_duration_seconds`
/// to avoid their attestation being invalidated.
pub attestation_max_validity_duration_seconds: Option<u64>,
}

/// Configuration parameters of the contract.
Expand Down Expand Up @@ -87,6 +91,10 @@ pub struct Config {
pub cleanup_orphaned_node_migrations_tera_gas: u64,
/// Prepaid gas for a `remove_non_participant_update_votes` call.
pub remove_non_participant_update_votes_tera_gas: u64,
/// Contract defined time to live for node attestations. Participants
/// must refresh their attestations before `attestation_max_validity_duration_seconds`
/// to avoid their attestation being invalidated.
pub attestation_max_validity_duration_seconds: u64,
}

#[cfg(test)]
Expand All @@ -108,6 +116,7 @@ mod tests {
clean_tee_status_tera_gas: Some(10),
cleanup_orphaned_node_migrations_tera_gas: Some(3),
remove_non_participant_update_votes_tera_gas: Some(5),
attestation_max_validity_duration_seconds: Some(1912312),
Copy link
Contributor

Choose a reason for hiding this comment

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

this equals to 22 days, why did you choose this number?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This test is just testing a serialization and deserialization works as expected. The values are not used for anything here.

};
let json = serde_json::to_string(&original_config).unwrap();
let serialized_and_deserialized_config: InitConfig = serde_json::from_str(&json).unwrap();
Expand Down Expand Up @@ -155,6 +164,7 @@ mod tests {
clean_tee_status_tera_gas: None,
cleanup_orphaned_node_migrations_tera_gas: None,
remove_non_participant_update_votes_tera_gas: None,
attestation_max_validity_duration_seconds: None,
};

assert_eq!(default_config, config_with_all_values_as_none);
Expand Down
10 changes: 10 additions & 0 deletions crates/contract/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ const DEFAULT_CLEANUP_ORPHANED_NODE_MIGRATIONS_TERA_GAS: u64 = 3;
/// Prepaid gas for a `remove_non_participant_update_votes` call
const DEFAULT_REMOVE_NON_PARTICIPANT_UPDATE_VOTES_TERA_GAS: u64 = 5;

// --- TEE configs --
/// The maximum duration a given attestation will be considered valid by the contract.
const DEFDAULT_ATTESTATION_MAX_VALIDITY_DURATION_SECONDS: u64 = 60 * 60 * 24 * 7; // 7 days

/// Config for V2 of the contract.
#[near(serializers=[borsh, json])]
#[derive(Clone, Debug, PartialEq, Eq)]
Expand All @@ -38,6 +42,10 @@ pub(crate) struct Config {
pub(crate) key_event_timeout_blocks: u64,
/// The grace period duration for expiry of old mpc image hashes once a new one is added.
pub(crate) tee_upgrade_deadline_duration_seconds: u64,
/// Contract defined time to live for node attestations. Participants
/// must refresh their attestations before `attestation_max_validity_duration_seconds`
/// to avoid their attestation being invalidated.
pub(crate) attestation_max_validity_duration_seconds: u64,
/// Amount of gas to deposit for contract and config updates.
pub(crate) contract_upgrade_deposit_tera_gas: u64,
/// Gas required for a sign request.
Expand Down Expand Up @@ -78,6 +86,8 @@ impl Default for Config {
DEFAULT_CLEANUP_ORPHANED_NODE_MIGRATIONS_TERA_GAS,
remove_non_participant_update_votes_tera_gas:
DEFAULT_REMOVE_NON_PARTICIPANT_UPDATE_VOTES_TERA_GAS,
attestation_max_validity_duration_seconds:
DEFDAULT_ATTESTATION_MAX_VALIDITY_DURATION_SECONDS,
}
}
}
136 changes: 25 additions & 111 deletions crates/contract/src/dto_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use contract_interface::types as dtos;
use mpc_attestation::{
attestation::{Attestation, DstackAttestation, MockAttestation},
attestation::{Attestation, DstackAttestation, MockAttestation, VerifiedAttestation},
collateral::{Collateral, QuoteCollateralV3},
EventLog, TcbInfo,
};
Expand Down Expand Up @@ -70,11 +70,11 @@ impl IntoContractType<MockAttestation> for dtos::MockAttestation {
dtos::MockAttestation::WithConstraints {
mpc_docker_image_hash,
launcher_docker_compose_hash,
expiry_time_stamp_seconds,
creation_time_stamp_seconds,
} => MockAttestation::WithConstraints {
mpc_docker_image_hash: mpc_docker_image_hash.map(Into::into),
launcher_docker_compose_hash: launcher_docker_compose_hash.map(Into::into),
expiry_time_stamp_seconds,
creation_time_stamp_seconds,
},
}
}
Expand Down Expand Up @@ -179,14 +179,21 @@ impl IntoContractType<EventLog> for dtos::EventLog {
}
}

impl IntoInterfaceType<dtos::Attestation> for Attestation {
fn into_dto_type(self) -> dtos::Attestation {
impl IntoInterfaceType<dtos::VerifiedAttestation> for VerifiedAttestation {
fn into_dto_type(self) -> dtos::VerifiedAttestation {
match self {
Attestation::Dstack(dstack_attestation) => {
dtos::Attestation::Dstack(dstack_attestation.into_dto_type())
VerifiedAttestation::Mock(mock_attestation) => {
dtos::VerifiedAttestation::Mock(mock_attestation.into_dto_type())
}
Attestation::Mock(mock_attestation) => {
dtos::Attestation::Mock(mock_attestation.into_dto_type())
VerifiedAttestation::Dstack(validated_dstack_attestation) => {
dtos::VerifiedAttestation::Dtack(dtos::VerifiedDstackAttestation {
mpc_image_hash: validated_dstack_attestation.mpc_image_hash.into(),
launcher_compose_hash: validated_dstack_attestation
.launcher_compose_hash
.into(),
creation_time_stamp_seonds: validated_dstack_attestation
.creation_time_stamp_seonds,
})
}
}
}
Expand All @@ -200,116 +207,16 @@ impl IntoInterfaceType<dtos::MockAttestation> for MockAttestation {
MockAttestation::WithConstraints {
mpc_docker_image_hash,
launcher_docker_compose_hash,
expiry_time_stamp_seconds,
creation_time_stamp_seconds,
} => dtos::MockAttestation::WithConstraints {
mpc_docker_image_hash: mpc_docker_image_hash.map(Into::into),
launcher_docker_compose_hash: launcher_docker_compose_hash.map(Into::into),
expiry_time_stamp_seconds,
creation_time_stamp_seconds,
},
}
}
}

impl IntoInterfaceType<dtos::DstackAttestation> for DstackAttestation {
fn into_dto_type(self) -> dtos::DstackAttestation {
let DstackAttestation {
quote,
collateral,
tcb_info,
} = self;

dtos::DstackAttestation {
quote: quote.into(),
collateral: collateral.into_dto_type(),
tcb_info: tcb_info.into_dto_type(),
}
}
}

impl IntoInterfaceType<dtos::Collateral> for Collateral {
fn into_dto_type(self) -> dtos::Collateral {
// Collateral is a newtype wrapper around QuoteCollateralV3
let QuoteCollateralV3 {
pck_crl_issuer_chain,
root_ca_crl,
pck_crl,
tcb_info_issuer_chain,
tcb_info,
tcb_info_signature,
qe_identity_issuer_chain,
qe_identity,
qe_identity_signature,
} = self.into();

dtos::Collateral {
pck_crl_issuer_chain,
root_ca_crl,
pck_crl,
tcb_info_issuer_chain,
tcb_info,
tcb_info_signature,
qe_identity_issuer_chain,
qe_identity,
qe_identity_signature,
}
}
}

impl IntoInterfaceType<dtos::TcbInfo> for TcbInfo {
fn into_dto_type(self) -> dtos::TcbInfo {
let TcbInfo {
mrtd,
rtmr0,
rtmr1,
rtmr2,
rtmr3,
os_image_hash,
compose_hash,
device_id,
app_compose,
event_log,
} = self;

let event_log = event_log
.into_iter()
.map(IntoInterfaceType::into_dto_type)
.collect();

dtos::TcbInfo {
mrtd,
rtmr0,
rtmr1,
rtmr2,
rtmr3,
os_image_hash,
compose_hash,
device_id,
app_compose,
event_log,
}
}
}

impl IntoInterfaceType<dtos::EventLog> for EventLog {
fn into_dto_type(self) -> dtos::EventLog {
let EventLog {
imr,
event_type,
digest,
event,
event_payload,
} = self;

dtos::EventLog {
imr,
event_type,
digest,
event,
event_payload,
}
}
}

impl IntoInterfaceType<dtos::Secp256k1PublicKey> for &k256_types::PublicKey {
fn into_dto_type(self) -> dtos::Secp256k1PublicKey {
let mut bytes = [0u8; 64];
Expand Down Expand Up @@ -480,6 +387,9 @@ impl From<contract_interface::types::InitConfig> for Config {
if let Some(v) = config_ext.remove_non_participant_update_votes_tera_gas {
config.remove_non_participant_update_votes_tera_gas = v;
}
if let Some(v) = config_ext.attestation_max_validity_duration_seconds {
config.attestation_max_validity_duration_seconds = v;
}

config
}
Expand All @@ -505,6 +415,8 @@ impl From<&Config> for contract_interface::types::Config {
.cleanup_orphaned_node_migrations_tera_gas,
remove_non_participant_update_votes_tera_gas: value
.remove_non_participant_update_votes_tera_gas,
attestation_max_validity_duration_seconds: value
.attestation_max_validity_duration_seconds,
}
}
}
Expand All @@ -529,6 +441,8 @@ impl From<contract_interface::types::Config> for Config {
.cleanup_orphaned_node_migrations_tera_gas,
remove_non_participant_update_votes_tera_gas: value
.remove_non_participant_update_votes_tera_gas,
attestation_max_validity_duration_seconds: value
.attestation_max_validity_duration_seconds,
}
}
}
Loading
Loading