-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathlib.rs
More file actions
533 lines (452 loc) · 15.5 KB
/
lib.rs
File metadata and controls
533 lines (452 loc) · 15.5 KB
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
/*
Copyright 2021 Integritee AG and Supercomputing Systems AG
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#![feature(structural_match)]
#![feature(rustc_attrs)]
#![feature(core_intrinsics)]
#![feature(derive_eq)]
#![feature(trait_alias)]
#![crate_name = "enclave_runtime"]
#![crate_type = "staticlib"]
#![cfg_attr(not(target_env = "sgx"), no_std)]
#![cfg_attr(target_env = "sgx", feature(rustc_private))]
#![allow(clippy::missing_safety_doc)]
#[cfg(not(target_env = "sgx"))]
#[macro_use]
extern crate sgx_tstd as std;
use crate::{
error::{Error, Result},
initialization::global_components::{
GLOBAL_FULL_PARACHAIN_HANDLER_COMPONENT, GLOBAL_FULL_SOLOCHAIN_HANDLER_COMPONENT,
GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT,
GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT,
},
rpc::worker_api_direct::sidechain_io_handler,
utils::{
get_node_metadata_repository_from_solo_or_parachain,
get_triggered_dispatcher_from_solo_or_parachain, utf8_str_from_raw, DecodeRaw,
},
};
use codec::Decode;
use itc_parentchain::block_import_dispatcher::{
triggered_dispatcher::TriggerParentchainBlockImport, DispatchBlockImport,
};
use itp_component_container::ComponentGetter;
use itp_import_queue::PushToQueue;
use itp_node_api::metadata::NodeMetadata;
use itp_nonce_cache::{MutateNonce, Nonce, GLOBAL_NONCE_CACHE};
use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode, WorkerModeProvider};
use itp_sgx_crypto::key_repository::AccessPubkey;
use itp_storage::{StorageProof, StorageProofChecker};
use itp_types::{ShardIdentifier, SignedBlock};
use itp_utils::write_slice_and_whitespace_pad;
use log::*;
use once_cell::sync::OnceCell;
use sgx_types::sgx_status_t;
use sp_runtime::traits::BlakeTwo256;
use std::{
boxed::Box,
path::PathBuf,
slice,
string::{String, ToString},
vec::Vec,
};
mod attestation;
mod empty_impls;
mod initialization;
mod ipfs;
mod ocall;
mod utils;
pub mod error;
pub mod rpc;
mod sync;
mod tls_ra;
pub mod top_pool_execution;
#[cfg(feature = "teeracle")]
pub mod teeracle;
#[cfg(feature = "test")]
pub mod test;
pub type Hash = sp_core::H256;
pub type AuthorityPair = sp_core::ed25519::Pair;
static BASE_PATH: OnceCell<PathBuf> = OnceCell::new();
fn get_base_path() -> Result<PathBuf> {
let base_path = BASE_PATH.get().ok_or_else(|| {
Error::Other("BASE_PATH not initialized. Broken enclave init flow!".to_string().into())
})?;
Ok(base_path.clone())
}
/// Initialize the enclave.
#[no_mangle]
pub unsafe extern "C" fn init(
mu_ra_addr: *const u8,
mu_ra_addr_size: u32,
untrusted_worker_addr: *const u8,
untrusted_worker_addr_size: u32,
encoded_base_dir_str: *const u8,
encoded_base_dir_size: u32,
) -> sgx_status_t {
// Initialize the logging environment in the enclave.
env_logger::init();
let mu_ra_url =
match String::decode(&mut slice::from_raw_parts(mu_ra_addr, mu_ra_addr_size as usize))
.map_err(Error::Codec)
{
Ok(addr) => addr,
Err(e) => return e.into(),
};
let untrusted_worker_url = match String::decode(&mut slice::from_raw_parts(
untrusted_worker_addr,
untrusted_worker_addr_size as usize,
))
.map_err(Error::Codec)
{
Ok(addr) => addr,
Err(e) => return e.into(),
};
let base_dir = match String::decode(&mut slice::from_raw_parts(
encoded_base_dir_str,
encoded_base_dir_size as usize,
))
.map_err(Error::Codec)
{
Ok(b) => b,
Err(e) => return e.into(),
};
info!("Setting base_dir to {}", base_dir);
let path = PathBuf::from(base_dir);
BASE_PATH.set(path.clone()).expect("We only init this once here; qed.");
match initialization::init_enclave(mu_ra_url, untrusted_worker_url, path) {
Err(e) => e.into(),
Ok(()) => sgx_status_t::SGX_SUCCESS,
}
}
#[no_mangle]
pub unsafe extern "C" fn get_rsa_encryption_pubkey(
pubkey: *mut u8,
pubkey_size: u32,
) -> sgx_status_t {
let shielding_key_repository = match GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get() {
Ok(s) => s,
Err(e) => {
error!("{:?}", e);
return sgx_status_t::SGX_ERROR_UNEXPECTED
},
};
let rsa_pubkey = match shielding_key_repository.retrieve_pubkey() {
Ok(key) => key,
Err(e) => return e.into(),
};
let rsa_pubkey_json = match serde_json::to_string(&rsa_pubkey) {
Ok(k) => k,
Err(x) => {
println!("[Enclave] can't serialize rsa_pubkey {:?} {}", rsa_pubkey, x);
return sgx_status_t::SGX_ERROR_UNEXPECTED
},
};
let pubkey_slice = slice::from_raw_parts_mut(pubkey, pubkey_size as usize);
if let Err(e) =
write_slice_and_whitespace_pad(pubkey_slice, rsa_pubkey_json.as_bytes().to_vec())
{
return Error::Other(Box::new(e)).into()
};
sgx_status_t::SGX_SUCCESS
}
#[no_mangle]
pub unsafe extern "C" fn get_ecc_signing_pubkey(pubkey: *mut u8, pubkey_size: u32) -> sgx_status_t {
let signing_key_repository = match GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get() {
Ok(s) => s,
Err(e) => {
error!("{:?}", e);
return sgx_status_t::SGX_ERROR_UNEXPECTED
},
};
let signer_public = match signing_key_repository.retrieve_pubkey() {
Ok(s) => s,
Err(e) => return e.into(),
};
debug!("Restored ECC pubkey: {:?}", signer_public);
let pubkey_slice = slice::from_raw_parts_mut(pubkey, pubkey_size as usize);
pubkey_slice.clone_from_slice(&signer_public);
sgx_status_t::SGX_SUCCESS
}
#[no_mangle]
pub unsafe extern "C" fn set_nonce(nonce: *const u32) -> sgx_status_t {
log::info!("[Ecall Set Nonce] Setting the nonce of the enclave to: {}", *nonce);
let mut nonce_lock = match GLOBAL_NONCE_CACHE.load_for_mutation() {
Ok(l) => l,
Err(e) => {
error!("Failed to set nonce in enclave: {:?}", e);
return sgx_status_t::SGX_ERROR_UNEXPECTED
},
};
*nonce_lock = Nonce(*nonce);
sgx_status_t::SGX_SUCCESS
}
#[no_mangle]
pub unsafe extern "C" fn set_node_metadata(
node_metadata: *const u8,
node_metadata_size: u32,
) -> sgx_status_t {
let mut node_metadata_slice = slice::from_raw_parts(node_metadata, node_metadata_size as usize);
let metadata = match NodeMetadata::decode(&mut node_metadata_slice).map_err(Error::Codec) {
Err(e) => {
error!("Failed to decode node metadata: {:?}", e);
return sgx_status_t::SGX_ERROR_UNEXPECTED
},
Ok(m) => m,
};
let node_metadata_repository = match get_node_metadata_repository_from_solo_or_parachain() {
Ok(r) => r,
Err(e) => {
error!("Component get failure: {:?}", e);
return sgx_status_t::SGX_ERROR_UNEXPECTED
},
};
node_metadata_repository.set_metadata(metadata);
info!("Successfully set the node meta data");
sgx_status_t::SGX_SUCCESS
}
/// This is reduced to the sidechain block import RPC interface (i.e. worker-worker communication).
/// The entire rest of the RPC server is run inside the enclave and does not use this e-call function anymore.
#[no_mangle]
pub unsafe extern "C" fn call_rpc_methods(
request: *const u8,
request_len: u32,
response: *mut u8,
response_len: u32,
) -> sgx_status_t {
let request = match utf8_str_from_raw(request, request_len as usize) {
Ok(req) => req,
Err(e) => {
error!("[SidechainRpc] FFI: Invalid utf8 request: {:?}", e);
return sgx_status_t::SGX_ERROR_UNEXPECTED
},
};
let res = match sidechain_rpc_int(request) {
Ok(res) => res,
Err(e) => {
error!("RPC request failed: {:?}", e);
return e.into()
},
};
let response_slice = slice::from_raw_parts_mut(response, response_len as usize);
if let Err(e) = write_slice_and_whitespace_pad(response_slice, res.into_bytes()) {
return Error::Other(Box::new(e)).into()
};
sgx_status_t::SGX_SUCCESS
}
fn sidechain_rpc_int(request: &str) -> Result<String> {
let sidechain_block_import_queue = GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT.get()?;
let io = sidechain_io_handler(move |signed_block| {
sidechain_block_import_queue.push_single(signed_block)
});
// note: errors are still returned as Option<String>
Ok(io
.handle_request_sync(request)
.unwrap_or_else(|| format!("Empty rpc response for request: {}", request)))
}
/// Initialize sidechain enclave components.
///
/// Call this once at startup. Has to be called AFTER the light-client
/// (parentchain components) have been initialized (because we need the parentchain
/// block import dispatcher).
#[no_mangle]
pub unsafe extern "C" fn init_enclave_sidechain_components() -> sgx_status_t {
if let Err(e) = initialization::init_enclave_sidechain_components() {
error!("Failed to initialize sidechain components: {:?}", e);
return sgx_status_t::SGX_ERROR_UNEXPECTED
}
sgx_status_t::SGX_SUCCESS
}
/// Call this once at worker startup to initialize the TOP pool and direct invocation RPC server.
///
/// This function will run the RPC server on the same thread as it is called and will loop there.
/// That means that this function will not return as long as the RPC server is running. The calling
/// code should therefore spawn a new thread when calling this function.
#[no_mangle]
pub unsafe extern "C" fn init_direct_invocation_server(
server_addr: *const u8,
server_addr_size: usize,
) -> sgx_status_t {
let mut server_addr_encoded = slice::from_raw_parts(server_addr, server_addr_size);
let server_addr = match String::decode(&mut server_addr_encoded) {
Ok(s) => s,
Err(e) => {
error!("Decoding RPC server address failed. Error: {:?}", e);
return sgx_status_t::SGX_ERROR_UNEXPECTED
},
};
if let Err(e) = initialization::init_direct_invocation_server(server_addr) {
error!("Failed to initialize direct invocation server: {:?}", e);
return sgx_status_t::SGX_ERROR_UNEXPECTED
}
sgx_status_t::SGX_SUCCESS
}
#[no_mangle]
pub unsafe extern "C" fn init_parentchain_components(
params: *const u8,
params_size: usize,
latest_header: *mut u8,
latest_header_size: usize,
) -> sgx_status_t {
info!("Initializing light client!");
let encoded_params = slice::from_raw_parts(params, params_size);
let latest_header_slice = slice::from_raw_parts_mut(latest_header, latest_header_size);
match init_parentchain_params_internal(encoded_params.to_vec(), latest_header_slice) {
Ok(()) => sgx_status_t::SGX_SUCCESS,
Err(e) => e.into(),
}
}
/// Initializes the parentchain components and writes the latest header into the `latest_header` slice.
fn init_parentchain_params_internal(params: Vec<u8>, latest_header: &mut [u8]) -> Result<()> {
use initialization::parentchain::init_parentchain_components;
let encoded_latest_header =
init_parentchain_components::<WorkerModeProvider>(get_base_path()?, params)?;
write_slice_and_whitespace_pad(latest_header, encoded_latest_header)?;
Ok(())
}
#[no_mangle]
pub unsafe extern "C" fn init_shard(shard: *const u8, shard_size: u32) -> sgx_status_t {
let shard_identifier =
ShardIdentifier::from_slice(slice::from_raw_parts(shard, shard_size as usize));
if let Err(e) = initialization::init_shard(shard_identifier) {
error!("Failed to initialize shard ({:?}): {:?}", shard_identifier, e);
return sgx_status_t::SGX_ERROR_UNEXPECTED
}
sgx_status_t::SGX_SUCCESS
}
#[no_mangle]
pub unsafe extern "C" fn sync_parentchain(
blocks_to_sync: *const u8,
blocks_to_sync_size: usize,
events_to_sync: *const u8,
events_to_sync_size: usize,
events_proofs_to_sync: *const u8,
events_proofs_to_sync_size: usize,
_nonce: *const u32,
) -> sgx_status_t {
let blocks_to_sync = match Vec::<SignedBlock>::decode_raw(blocks_to_sync, blocks_to_sync_size) {
Ok(blocks) => blocks,
Err(e) => return Error::Codec(e).into(),
};
let events_proofs_to_sync =
match Vec::<StorageProof>::decode_raw(events_proofs_to_sync, events_proofs_to_sync_size) {
Ok(events_proofs) => events_proofs,
Err(e) => return Error::Codec(e).into(),
};
let blocks_to_sync_merkle_roots: Vec<sp_core::H256> =
blocks_to_sync.iter().map(|block| block.block.header.state_root).collect();
if let Err(e) = validate_events(&events_proofs_to_sync, &blocks_to_sync_merkle_roots) {
return e.into()
}
let events_to_sync = match Vec::<Vec<u8>>::decode_raw(events_to_sync, events_to_sync_size) {
Ok(events) => events,
Err(e) => return Error::Codec(e).into(),
};
if let Err(e) =
dispatch_parentchain_blocks_for_import::<WorkerModeProvider>(blocks_to_sync, events_to_sync)
{
return e.into()
}
sgx_status_t::SGX_SUCCESS
}
/// Dispatch the parentchain blocks for import.
/// Depending on the worker mode, a different dispatcher is used:
///
/// * An immediate dispatcher will immediately import any parentchain blocks and execute
/// the corresponding extrinsics (offchain-worker executor).
/// * The sidechain uses a triggered dispatcher, where the import of a parentchain block is
/// synchronized and triggered by the sidechain block production cycle.
///
fn dispatch_parentchain_blocks_for_import<WorkerModeProvider: ProvideWorkerMode>(
blocks_to_sync: Vec<SignedBlock>,
events_to_sync: Vec<Vec<u8>>,
) -> Result<()> {
if WorkerModeProvider::worker_mode() == WorkerMode::Teeracle {
trace!("Not importing any parentchain blocks");
return Ok(())
}
let import_dispatcher =
if let Ok(solochain_handler) = GLOBAL_FULL_SOLOCHAIN_HANDLER_COMPONENT.get() {
solochain_handler.import_dispatcher.clone()
} else if let Ok(parachain_handler) = GLOBAL_FULL_PARACHAIN_HANDLER_COMPONENT.get() {
parachain_handler.import_dispatcher.clone()
} else {
return Err(Error::NoParentchainAssigned)
};
import_dispatcher.dispatch_import(blocks_to_sync, events_to_sync)?;
Ok(())
}
/// Validates the events coming from the parentchain
fn validate_events(
events_proofs: &Vec<StorageProof>,
blocks_merkle_roots: &Vec<sp_core::H256>,
) -> Result<()> {
info!(
"Validating events, events_proofs_length: {:?}, blocks_merkle_roots_lengths: {:?}",
events_proofs.len(),
blocks_merkle_roots.len()
);
if events_proofs.len() != blocks_merkle_roots.len() {
return Err(Error::ParentChainSync)
}
let events_key = itp_storage::storage_value_key("System", "Events");
let validated_events: Result<Vec<Vec<u8>>> = events_proofs
.iter()
.zip(blocks_merkle_roots.iter())
.map(|(proof, root)| {
StorageProofChecker::<BlakeTwo256>::check_proof(
*root,
events_key.as_slice(),
proof.clone(),
)
.ok()
.flatten()
.ok_or_else(|| Error::ParentChainValidation(itp_storage::Error::WrongValue))
})
.collect();
let _ = validated_events?;
Ok(())
}
/// Triggers the import of parentchain blocks when using a queue to sync parentchain block import
/// with sidechain block production.
///
/// This trigger is only useful in combination with a `TriggeredDispatcher` and sidechain. In case no
/// sidechain and the `ImmediateDispatcher` are used, this function is obsolete.
#[no_mangle]
pub unsafe extern "C" fn trigger_parentchain_block_import() -> sgx_status_t {
match internal_trigger_parentchain_block_import() {
Ok(()) => sgx_status_t::SGX_SUCCESS,
Err(e) => {
error!("Failed to trigger import of parentchain blocks: {:?}", e);
sgx_status_t::SGX_ERROR_UNEXPECTED
},
}
}
fn internal_trigger_parentchain_block_import() -> Result<()> {
let triggered_import_dispatcher = get_triggered_dispatcher_from_solo_or_parachain()?;
triggered_import_dispatcher.import_all()?;
Ok(())
}
// This is required, because `ring` / `ring-xous` would not compile without it non-release (debug) mode.
// See #1200 for more details.
#[cfg(debug_assertions)]
#[no_mangle]
pub extern "C" fn __assert_fail(
__assertion: *const u8,
__file: *const u8,
__line: u32,
__function: *const u8,
) -> ! {
use core::intrinsics::abort;
abort()
}