-
Notifications
You must be signed in to change notification settings - Fork 2
/
competition.rs
187 lines (162 loc) · 5.79 KB
/
competition.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
use std::time::SystemTime;
use oasis_std::{abi::*, collections::Map, Address, Context, Event};
#[derive(oasis_std::Service)]
struct Competition {
/// The address of the Participant service used to authenticate and manage users.
user_registry: Address,
/// Public data used to train submitted models.
train_dataset: AuthenticatedData,
/// Private data that will be used by the evaluation program to score results.
test_dataset: EncryptedData,
/// The evaluation program that will be used to score the submissions.
/// The evaluation program runs on trusted hardware and (by providing its attestation)
/// is the only entity authorized to declare the winner of the competition.
evaluation_program: AuthenticatedData,
/// A collection of participant's current submission.
submissions: Map<String /* username */, EncryptedData /* the model and its params */>,
/// Unix timestamp at which this competition closes and submissions can be evaluated.
end_timestamp: u64,
}
#[derive(Serialize, Deserialize)]
pub struct AuthenticatedData {
url: String,
/// The expected hash of the data (or the measurement, if the data is a program).
hash: Vec<u8>,
}
/// An on-chain pointer to encrypted off-chain data.
/// The key is only made available to the evaluation program after successful attestation.
#[derive(Clone, Serialize, Deserialize)]
pub struct EncryptedData {
url: String,
cipher: Aes256GcmParams,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct Aes256GcmParams {
key: Vec<u8>,
iv: Vec<u8>,
tag: Vec<u8>,
}
impl Competition {
pub fn new(
_ctx: &Context,
user_registry: Address,
train_dataset: AuthenticatedData,
test_dataset: EncryptedData,
evaluation_program: AuthenticatedData,
end_timestamp: u64,
) -> Self {
Self {
user_registry,
train_dataset,
test_dataset,
evaluation_program,
end_timestamp,
submissions: Map::new(),
}
}
/// All state on the Oasis platform is confidential, so we add an explicit getter.
pub fn get_public_state(&self, _ctx: &Context) -> PublicState {
PublicState {
user_registry: &self.user_registry,
train_dataset: &self.train_dataset,
evaluation_program: &self.evaluation_program,
end_timestamp: self.end_timestamp,
}
}
/// Submits a model to the competition.
/// The `participant_auth_token` should have been received from the Competition's
/// UserRegistry.
pub fn submit(
&mut self,
_ctx: &Context,
participant_auth_token: String,
model: EncryptedData,
) -> Result<(), Error> {
if !self.is_accepting_submissions() {
return Err(Error::SubmissionsClosed);
}
let reg_client = user_registry::UserRegistryClient::new(self.user_registry);
match reg_client.verify_token(&Context::default(), &participant_auth_token) {
Ok(Ok(user_info)) => {
self.submissions.insert(user_info.name, model);
Ok(())
}
Ok(Err(_)) => Err(Error::PermissionDenied),
Err(_) => Err(Error::ParticipantRegistryUnreachable),
}
}
fn is_accepting_submissions(&self) -> bool {
let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
current_time.unwrap().as_secs() < self.end_timestamp
}
}
impl Competition {
//! Evaluation methods. Can only be called by an attested evaluation program enclave.
/// Returns the secret data needed to evaluate the submissions to the attested
/// evaluation program enclave.
pub fn begin_evaluation(
&self,
_ctx: &Context,
attestation: AttestationReport,
) -> Result<EvaluationSecrets, Error> {
self.authorize_evaluation_program(&attestation)?;
Ok(EvaluationSecrets {
test_dataset: self.test_dataset.clone(),
submissions: self.submissions.clone(),
})
}
/// Emits an event that (publicly) announces the winner of the competition.
pub fn announce_winner(
&self,
_ctx: &Context,
attestation: AttestationReport,
winner: String,
) -> Result<(), Error> {
self.authorize_evaluation_program(&attestation)?;
Event::emit(&CompetitionCompleted { winner: &winner });
Ok(())
}
/// Authorizes an evaluation program enclave by validating its attestation against
/// the expected signature and measurement.
fn authorize_evaluation_program(&self, attestation: &AttestationReport) -> Result<(), Error> {
// if this were a real attestation, we'd validate the signature, but you get the idea.
if self.is_accepting_submissions()
|| attestation.measurement != self.evaluation_program.hash
{
return Err(Error::PermissionDenied);
}
Ok(())
}
}
#[derive(Serialize, Deserialize)]
pub struct AttestationReport {
measurement: Vec<u8>,
signature: Vec<u8>,
}
#[derive(Serialize, Deserialize)]
pub struct EvaluationSecrets {
test_dataset: EncryptedData,
submissions: Map<String, EncryptedData>,
}
#[derive(Serialize)]
pub enum Error {
ParticipantRegistryUnreachable,
PermissionDenied,
SubmissionsClosed,
}
#[derive(Serialize, Event)]
pub struct CompetitionCompleted<'a> {
/// The username of the participant who made the winning submission.
winner: &'a String,
}
/// The public state of this service that can safely be returned to clients.
#[derive(Serialize)]
pub struct PublicState<'a> {
user_registry: &'a Address,
train_dataset: &'a AuthenticatedData,
evaluation_program: &'a AuthenticatedData,
end_timestamp: u64,
}
fn main() {
oasis_std::service!(Competition);
}