Skip to content

Commit 7b08d07

Browse files
authored
automatic-shard-joining-and-per-shard-updates-on-block (#160)
* [enclave] update_map contains options. This is needed if a storage value needs to be deleted in the STF [enclave, stf] perform state updates per shard, auto join new shards * [worker] only feed 100 blocks at a time into the chain relay. Improved logging while syncing to keep track of sync status * [WorkerApi] Remove default protocol ws.
1 parent 48f5199 commit 7b08d07

File tree

7 files changed

+115
-74
lines changed

7 files changed

+115
-74
lines changed

client/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ fn main() {
9999
.global(true)
100100
.takes_value(true)
101101
.value_name("STRING")
102-
.default_value("127.0.0.1")
102+
.default_value("ws://127.0.0.1")
103103
.help("worker url"),
104104
)
105105
.arg(

enclave/src/lib.rs

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ use chain_relay::{
6262
use sp_runtime::OpaqueExtrinsic;
6363
use sp_runtime::{generic::SignedBlock, traits::Header as HeaderT};
6464
use substrate_api_client::extrinsic::xt_primitives::UncheckedExtrinsicV4;
65-
use substratee_stf::sgx::OpaqueCall;
65+
use substratee_stf::sgx::{OpaqueCall, shards_key_hash, storage_hashes_to_update_per_shard};
6666

6767
mod aes;
6868
mod attestation;
@@ -218,9 +218,16 @@ pub unsafe extern "C" fn get_state(
218218
return sgx_status_t::SGX_ERROR_UNEXPECTED;
219219
}
220220

221+
if !state::exists(&shard) {
222+
info!("Initialized new shard that was queried chain: {:?}", shard);
223+
if let Err(e) = state::init_shard(&shard) {
224+
return e;
225+
}
226+
}
227+
221228
let mut state = match state::load(&shard) {
222229
Ok(s) => s,
223-
Err(status) => return status,
230+
Err(status) => return status
224231
};
225232

226233
let validator = match io::light_validation::unseal() {
@@ -348,14 +355,15 @@ pub unsafe extern "C" fn sync_chain_relay(
348355
return sgx_status_t::SGX_ERROR_UNEXPECTED;
349356
}
350357

358+
if update_states(signed_block.block.header.clone()).is_err() {
359+
error!("Error performing state updates upon block import")
360+
}
361+
351362
match scan_block_for_relevant_xt(&signed_block.block) {
352363
Ok(c) => calls.extend(c.into_iter()),
353364
Err(_) => error!("Error executing relevant extrinsics"),
354365
};
355366

356-
if update_states(signed_block.block.header).is_err() {
357-
error!("Error performing state updates upon block import")
358-
}
359367
}
360368

361369
if let Err(_e) = stf_post_actions(validator, calls, xt_slice, *nonce) {
@@ -376,16 +384,37 @@ pub fn update_states(header: Header) -> SgxResult<()> {
376384
return Ok(());
377385
}
378386

387+
// global requests they are the same for every shard
379388
let responses: Vec<WorkerResponse<Vec<u8>>> = worker_request(requests)?;
380-
let update_map = verify_worker_responses(responses, header)?;
381-
382-
let shards = state::list_shards()?;
383-
debug!("found shards: {:?}", shards);
384-
for s in shards {
385-
let mut state = state::load(&s)?;
386-
Stf::update_storage(&mut state, &update_map);
387-
state::write(state, &s)?;
388-
}
389+
let update_map = verify_worker_responses(responses, header.clone())?;
390+
// look for new shards an initialize them
391+
if let Some(maybe_shards) = update_map.get(&shards_key_hash()) {
392+
match maybe_shards {
393+
Some(shards) => {
394+
let shards: Vec<ShardIdentifier> = Decode::decode(&mut shards.as_slice()).sgx_error_with_log("error decoding shards")?;
395+
for s in shards {
396+
if !state::exists(&s) {
397+
info!("Initialized new shard that was found on chain: {:?}", s);
398+
state::init_shard(&s)?;
399+
}
400+
// per shard (cid) requests
401+
let per_shard_request = storage_hashes_to_update_per_shard(&s)
402+
.into_iter()
403+
.map(|key| WorkerRequest::ChainStorage(key, Some(header.hash())))
404+
.collect();
405+
406+
let responses: Vec<WorkerResponse<Vec<u8>>> = worker_request(per_shard_request)?;
407+
let per_shard_update_map = verify_worker_responses(responses, header.clone())?;
408+
409+
let mut state = state::load(&s)?;
410+
Stf::update_storage(&mut state, &per_shard_update_map);
411+
Stf::update_storage(&mut state, &update_map);
412+
state::write(state, &s)?;
413+
}
414+
}
415+
None => info!("No shards are on the chain yet")
416+
};
417+
};
389418
Ok(())
390419
}
391420

@@ -500,12 +529,7 @@ fn handle_call_worker_xt(
500529
return Ok(());
501530
}
502531

503-
let mut state = if state::exists(&shard) {
504-
state::load(&shard)?
505-
} else {
506-
state::init_shard(&shard)?;
507-
Stf::init_state()
508-
};
532+
let mut state = state::load(&shard)?;
509533

510534
debug!("Update STF storage!");
511535
let requests = Stf::get_storage_hashes_to_update(&stf_call_signed)
@@ -541,7 +565,7 @@ fn handle_call_worker_xt(
541565
fn verify_worker_responses(
542566
responses: Vec<WorkerResponse<Vec<u8>>>,
543567
header: Header,
544-
) -> SgxResult<HashMap<Vec<u8>, Vec<u8>>> {
568+
) -> SgxResult<HashMap<Vec<u8>, Option<Vec<u8>>>> {
545569
let mut update_map = HashMap::new();
546570
for response in responses.iter() {
547571
match response {
@@ -562,10 +586,7 @@ fn verify_worker_responses(
562586
error!("Wrong storage value supplied");
563587
return Err(sgx_status_t::SGX_ERROR_UNEXPECTED);
564588
}
565-
566-
if let Some(val) = value {
567-
update_map.insert(key.clone(), val.clone());
568-
}
589+
update_map.insert(key.clone(), value.clone());
569590
}
570591
}
571592
}
@@ -658,7 +679,7 @@ fn worker_request<V: Encode + Decode>(
658679
req: Vec<WorkerRequest>,
659680
) -> SgxResult<Vec<WorkerResponse<V>>> {
660681
let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED;
661-
let mut resp: Vec<u8> = vec![0; 4196];
682+
let mut resp: Vec<u8> = vec![0; 4196 * 4];
662683

663684
let res = unsafe {
664685
ocall_worker_request(

enclave/src/state.rs

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
use std::fs;
1919
use std::vec::Vec;
20+
use std::io::Write;
2021

2122
use log::*;
2223
use sgx_tcrypto::rsgx_sha256_slice;
@@ -27,8 +28,8 @@ use crate::constants::{ENCRYPTED_STATE_FILE, SHARDS_PATH};
2728
use crate::hex;
2829
use crate::io;
2930
use crate::utils::UnwrapOrSgxErrorUnexpected;
30-
use base58::{FromBase58, ToBase58};
31-
use codec::{Decode, Encode};
31+
use base58::ToBase58;
32+
use codec::Encode;
3233
use sgx_externalities::SgxExternalitiesTrait;
3334
use sp_core::H256;
3435
use std::path::Path;
@@ -93,7 +94,10 @@ pub fn exists(shard: &ShardIdentifier) -> bool {
9394
}
9495

9596
pub fn init_shard(shard: &ShardIdentifier) -> SgxResult<()> {
96-
fs::create_dir_all(format!("{}/{}", SHARDS_PATH, shard.encode().to_base58())).sgx_error()
97+
let path = format!("{}/{}", SHARDS_PATH, shard.encode().to_base58());
98+
fs::create_dir_all(path.clone()).sgx_error()?;
99+
let mut file = fs::File::create(format!("{}/{}", path, ENCRYPTED_STATE_FILE)).sgx_error()?;
100+
file.write_all(b"").sgx_error()
97101
}
98102

99103
fn read(path: &str) -> SgxResult<Vec<u8>> {
@@ -125,22 +129,6 @@ fn encrypt(mut state: Vec<u8>) -> SgxResult<Vec<u8>> {
125129
Ok(state)
126130
}
127131

128-
pub fn list_shards() -> SgxResult<Vec<ShardIdentifier>> {
129-
let files = fs::read_dir(SHARDS_PATH).sgx_error()?;
130-
let mut shards = Vec::new();
131-
for file in files {
132-
let s = file
133-
.sgx_error()?
134-
.file_name()
135-
.into_string()
136-
.sgx_error()?
137-
.from_base58()
138-
.sgx_error()?;
139-
shards.push(ShardIdentifier::decode(&mut s.as_slice()).sgx_error()?);
140-
}
141-
Ok(shards)
142-
}
143-
144132
pub fn test_encrypted_state_io_works() {
145133
let path = "test_state_file.bin";
146134
let plaintext = b"The quick brown fox jumps over the lazy dog.";

stf/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ pub enum TrustedOperationSigned {
6363
get(TrustedGetterSigned),
6464
}
6565

66-
#[derive(Encode, Decode, Clone)]
66+
#[derive(Encode, Decode, Clone, Debug)]
6767
#[allow(non_camel_case_types)]
6868
pub enum TrustedCall {
6969
balance_set_balance(AccountId, AccountId, Balance, Balance),

stf/src/sgx.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use sp_runtime::traits::Dispatchable;
1313

1414
use crate::{
1515
AccountId, State, Stf, TrustedCall, TrustedCallSigned, TrustedGetter, TrustedGetterSigned,
16-
SUBSRATEE_REGISTRY_MODULE, UNSHIELD,
16+
ShardIdentifier, SUBSRATEE_REGISTRY_MODULE, UNSHIELD,
1717
};
1818
use sp_core::blake2_256;
1919

@@ -69,11 +69,16 @@ impl Stf {
6969
ext
7070
}
7171

72-
pub fn update_storage(ext: &mut State, map_update: &HashMap<Vec<u8>, Vec<u8>>) {
72+
pub fn update_storage(ext: &mut State, map_update: &HashMap<Vec<u8>, Option<Vec<u8>>>) {
7373
ext.execute_with(|| {
7474
map_update
7575
.iter()
76-
.for_each(|(k, v)| sp_io::storage::set(k, v))
76+
.for_each(|(k, v)| {
77+
match v {
78+
Some(value) => sp_io::storage::set(k, value),
79+
None => sp_io::storage::clear(k)
80+
};
81+
});
7782
});
7883
}
7984

@@ -205,19 +210,28 @@ impl Stf {
205210
}
206211

207212
pub fn get_storage_hashes_to_update_for_getter(getter: &TrustedGetterSigned) -> Vec<Vec<u8>> {
208-
let key_hashes = Vec::new();
209-
info!("No storage updates needed for getter: {:?}", getter.getter); // dummy. Is currently not needed
210-
key_hashes
213+
info!("No specific storage updates needed for getter. Returning those for on block: {:?}", getter.getter);
214+
Self::storage_hashes_to_update_on_block()
211215
}
212216

213217
pub fn storage_hashes_to_update_on_block() -> Vec<Vec<u8>> {
214-
// let key_hashes = Vec::new();
215-
// key_hashes.push(storage_value_key("dummy", "dummy"));
216-
// key_hashes
217-
Vec::new()
218+
let mut key_hashes = Vec::new();
219+
220+
// get all shards that are currently registered
221+
key_hashes.push(shards_key_hash());
222+
223+
key_hashes
218224
}
219225
}
220226

227+
pub fn storage_hashes_to_update_per_shard(_shard: &ShardIdentifier) -> Vec<Vec<u8>> {
228+
Vec::new()
229+
}
230+
231+
pub fn shards_key_hash() -> Vec<u8> {
232+
storage_value_key("EncointerCurrencies", "CurrencyIdentifiers")
233+
}
234+
221235
// get the AccountInfo key where the nonce is stored
222236
pub fn nonce_key_hash(account: &AccountId) -> Vec<u8> {
223237
storage_map_key(

worker/src/main.rs

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,11 @@ fn worker(w_ip: &str, w_port: &str, mu_ra_port: &str, shard: &ShardIdentifier) {
289289
}
290290
}
291291

292+
println!("*** [+] finished remote attestation\n");
293+
294+
println!("*** Syncing chain relay\n\n");
292295
let mut latest_head = init_chain_relay(eid, &api);
296+
println!("*** [+] Finished syncing chain relay\n");
293297

294298
// ------------------------------------------------------------------------
295299
// subscribe to events and react on firing
@@ -470,33 +474,47 @@ pub fn sync_chain_relay(
470474

471475
// Todo: Check, is this dangerous such that it could be an eternal or too big loop?
472476
let mut head = curr_head.clone();
477+
478+
let no_blocks_to_sync = head.block.header.number - last_synced_head.number;
479+
if no_blocks_to_sync > 1 {
480+
println!("Chain Relay is synced until block: {:?}", last_synced_head.number);
481+
println!("Last finalized block number: {:?}\n", head.block.header.number);
482+
}
483+
473484
while head.block.header.parent_hash != last_synced_head.hash() {
474485
head = api
475486
.get_signed_block(Some(head.block.header.parent_hash))
476487
.unwrap();
477488
blocks_to_sync.push(head.clone());
478-
debug!("Syncing Block: {:?}", head.block)
489+
490+
if head.block.header.number % 100 == 0 {
491+
println!("Remaining blocks to fetch until last synced header: {:?}", head.block.header.number - last_synced_head.number)
492+
}
479493
}
480494
blocks_to_sync.reverse();
481-
debug!(
482-
"Got {} headers to sync in chain relay.",
483-
blocks_to_sync.len()
484-
);
485495

486496
let tee_accountid = enclave_account(eid);
487-
let tee_nonce = get_nonce(&api, &tee_accountid);
488497

489-
let xts = enclave_sync_chain_relay(eid, blocks_to_sync, tee_nonce).unwrap();
490-
491-
let extrinsics: Vec<Vec<u8>> = Decode::decode(&mut xts.as_slice()).unwrap();
492-
info!(
493-
"Sync chain relay: Enclave wants to send {} extrinsics",
494-
extrinsics.len()
495-
);
498+
// only feed 100 blocks at a time into the enclave to save enclave state regularly
499+
let mut i = blocks_to_sync[0].block.header.number as usize;
500+
for chunk in blocks_to_sync.chunks(100) {
501+
let tee_nonce = get_nonce(&api, &tee_accountid);
502+
let xts = enclave_sync_chain_relay(eid, chunk.to_vec(), tee_nonce).unwrap();
503+
let extrinsics: Vec<Vec<u8>> = Decode::decode(&mut xts.as_slice()).unwrap();
504+
505+
if !extrinsics.is_empty() {
506+
println!(
507+
"Sync chain relay: Enclave wants to send {} extrinsics",
508+
extrinsics.len()
509+
);
510+
}
511+
for xt in extrinsics.into_iter() {
512+
api.send_extrinsic(hex_encode(xt), XtStatus::InBlock)
513+
.unwrap();
514+
}
496515

497-
for xt in extrinsics.into_iter() {
498-
api.send_extrinsic(hex_encode(xt), XtStatus::InBlock)
499-
.unwrap();
516+
i += chunk.len();
517+
println!("Synced {} blocks out of {} finalized blocks", i , blocks_to_sync[0].block.header.number as usize + blocks_to_sync.len())
500518
}
501519

502520
curr_head.block.header

worker/worker-api/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ pub struct Api {
3939
impl Api {
4040
pub fn new(url: String) -> Api {
4141
Api {
42-
url: format!("ws://{}", url),
42+
url,
4343
}
4444
}
4545

0 commit comments

Comments
 (0)