Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
8550d7d
[itp-sgx-crypto] add `ToPubkey` and `AccessPubkey` traits
clangenb May 23, 2023
560c787
[itp-sgx-crypto] refactor the Rsa3072 stuff to no longer use static f…
clangenb May 23, 2023
2906f8f
[itp-sgx-crypto] set-base-path to the PWD
clangenb May 23, 2023
e9dbe6e
[enclave-runtime] more explanation about using the PWD
clangenb May 23, 2023
177503d
[enclave-runtime] add todo for replacing the once-cell.
clangenb May 23, 2023
5645420
taplo fmt
clangenb May 23, 2023
0c1d6b9
add some doc
clangenb May 23, 2023
8ea4fff
typo
clangenb May 23, 2023
b229b3e
Merge branch 'master' into cl/set-base-path-of-shielding-key
clangenb May 23, 2023
7a381e4
[sgx-crypto] log full path instead of just filename.
clangenb May 24, 2023
33faf7d
[itp-sgx-io] fix standalone compilation
clangenb May 24, 2023
19b873e
[itp-sgx-crypto] put some functions behind a trait.
clangenb May 24, 2023
b5c5284
[enclave-runtime/attestation_handler] add signing key repo to struct
clangenb May 24, 2023
6a71619
[itp-sgx-crypto] impl `ToPubkey` for `ed25511::Pair`
clangenb May 24, 2023
cc0be6e
introduce `SigningKeyRepository` and remove all instances of `StaticF…
clangenb May 24, 2023
f73b880
[itp-sgx-crypto] add base path to AESSeal
clangenb May 24, 2023
d0e5c3e
[itp-state-handler] wip update tests
clangenb May 24, 2023
11a3852
[itp-state-handler] add debug log for existing files in shard
clangenb May 24, 2023
a4d0a41
[itp-state-handler] fix tests by creating a unique key-repo per test
clangenb May 24, 2023
07891b6
Merge branch 'master' into cl/set-base-path-of-state-key
clangenb May 26, 2023
d53ef30
fix merge errors
clangenb May 26, 2023
df8c61f
[itp-sgx-crypto] add tests for aes
clangenb May 26, 2023
4202a29
taplo fmt
clangenb May 26, 2023
bace8d6
clippy
clangenb May 26, 2023
61688aa
move aes key file name constant to the aes module
clangenb May 26, 2023
df29524
[stf-state-handle] rename `TestKeyRepositoryMock` to `TestKeyReposito…
clangenb May 26, 2023
f916684
[itp-stf-state-handler] introduce `StatePathProvider`
clangenb May 26, 2023
a58ef02
[itp-stf-state-handler] add more methods to path provider
clangenb May 26, 2023
0724222
[itp-stf-state-handler/sgx-tests] remove obsolete `ShardDirectoryHand…
clangenb May 26, 2023
d41d9b3
[itp-sgx-crypto] more accurate name for the AES key file
clangenb May 26, 2023
26ece66
[itp-sgx-crypto] minor test fixes
clangenb May 26, 2023
6106469
[itp-state-handler/sgx_test] extract setup method
clangenb May 26, 2023
34ed2e6
[itp-state-handler/sgx_test] fix last test
clangenb May 26, 2023
28f61f9
Merge branch 'master' into cl/set-base-of-the-state
clangenb May 26, 2023
10d6b3f
minor cleanup
clangenb May 26, 2023
9ef3f20
[itp-stf-staten-handler] add minor docs.
clangenb May 26, 2023
d1ba94f
fix unused import
clangenb May 26, 2023
7752f5c
fix clippy
clangenb May 26, 2023
b53f108
minor typos
clangenb May 26, 2023
84879cc
[itp-stf-state-handler] rename `StatePathHelper` to `StateDir`
clangenb May 26, 2023
2a8d3de
[itp-stf-state-handler] move test methods of `StateDir` to its defini…
clangenb May 26, 2023
101867c
[itp-stf-state-handler] replace flat_map with filter_map
clangenb May 26, 2023
bc27cbc
[itp-stf-state-handler] fix: make `StateDir` test methods public
clangenb May 26, 2023
f43c5eb
[itp-stf-state-handler] remove unnecessary method.
clangenb May 26, 2023
a46f16a
[itp-stf-state-handler] annotate todo with issue number
clangenb May 26, 2023
baa01ad
[itp-stf-state-handler/file_io] refactor methods to be more ergonomic…
clangenb May 28, 2023
3940250
[itp-stf-state-handler/file_io] rename `directory_contains_state` to …
clangenb May 28, 2023
7f489a5
[itp-stf-state-handler/file_io] fix test by defaulting to empty vec i…
clangenb May 28, 2023
9418730
[itp-stf-state-handler/file_io] remove unnecessary nesting
clangenb May 28, 2023
27e613d
[itp-stf-state-handler/file_io] typo
clangenb May 28, 2023
b472e15
[itp-stf-state-handler/file_io] minor improvement in function
clangenb May 29, 2023
3476506
[itp-stf-state-handler/sgx_tests] rename setup -> test_setup
clangenb May 29, 2023
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
1 change: 0 additions & 1 deletion core-primitives/settings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ pub mod files {

// used by worker and enclave
pub const SHARDS_PATH: &str = "shards";
pub const ENCRYPTED_STATE_FILE: &str = "state.bin";
pub const LAST_SLOT_BIN: &str = "last_slot.bin";

#[cfg(feature = "production")]
Expand Down
4 changes: 3 additions & 1 deletion core-primitives/stf-state-handler/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub enum Error {
OsStringConversion,
#[error("SGX crypto error: {0}")]
CryptoError(itp_sgx_crypto::Error),
#[error("IO error: {0}")]
IO(std::io::Error),
#[error("SGX error, status: {0}")]
SgxError(sgx_status_t),
#[error(transparent)]
Expand All @@ -59,7 +61,7 @@ pub enum Error {

impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Self::Other(e.into())
Self::IO(e)
Copy link
Contributor

Choose a reason for hiding this comment

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

This was much needed, thanks for this!

}
}

Expand Down
236 changes: 137 additions & 99 deletions core-primitives/stf-state-handler/src/file_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,97 @@
use crate::sgx_reexport_prelude::*;

#[cfg(any(test, feature = "std"))]
use rust_base58::base58::ToBase58;
use rust_base58::base58::{FromBase58, ToBase58};

#[cfg(feature = "sgx")]
use base58::ToBase58;

#[cfg(any(test, feature = "sgx"))]
use itp_settings::files::ENCRYPTED_STATE_FILE;
use base58::{FromBase58, ToBase58};

#[cfg(any(test, feature = "sgx"))]
use std::string::String;

use crate::{error::Result, state_snapshot_primitives::StateId};
use codec::Encode;
use codec::{Decode, Encode};
// Todo: Can be migrated to here in the course of #1292.
use itp_settings::files::SHARDS_PATH;
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 will be removed at the very end of #1292. It is currently also needed in the service to clean-up files, but won't be anymore after that because we will be able to purge the data-dir altogether, so the service does no longer need to know the SHARDS_PATH.

The above will make the migration of SHARDS_PATH to this crate much simpler, so I decided to wait.

use itp_types::ShardIdentifier;
use log::error;
use std::{format, path::PathBuf, vec::Vec};
use std::{
format,
path::{Path, PathBuf},
vec::Vec,
};

/// File name of the encrypted state file.
///
/// It is also the suffix of all past snapshots.
pub const ENCRYPTED_STATE_FILE: &str = "state.bin";

/// Helps with file system operations of all files relevant for the State.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct StateDir {
base_path: PathBuf,
}

impl StateDir {
pub fn new(base_path: PathBuf) -> Self {
Self { base_path }
}

pub fn shards_directory(&self) -> PathBuf {
self.base_path.join(SHARDS_PATH)
}

pub fn shard_path(&self, shard: &ShardIdentifier) -> PathBuf {
self.shards_directory().join(shard.encode().to_base58())
}

pub fn list_shards(&self) -> Result<Vec<ShardIdentifier>> {
Ok(list_shards(&self.shards_directory())
.map(|iter| iter.collect())
// return an empty vec in case the directory does not exist.
.unwrap_or_default())
}

pub fn list_state_ids_for_shard(
&self,
shard_identifier: &ShardIdentifier,
) -> Result<Vec<StateId>> {
let shard_path = self.shard_path(shard_identifier);
Ok(state_ids_for_shard(shard_path.as_path())?.collect())
}

pub fn purge_shard_dir(&self, shard: &ShardIdentifier) {
let shard_dir_path = self.shard_path(shard);
if let Err(e) = std::fs::remove_dir_all(&shard_dir_path) {
error!("Failed to remove shard directory {:?}: {:?}", shard_dir_path, e);
}
}

pub fn shard_exists(&self, shard: &ShardIdentifier) -> bool {
let shard_path = self.shard_path(shard);
shard_path.exists() && shard_contains_valid_state_id(&shard_path)
}

pub fn create_shard(&self, shard: &ShardIdentifier) -> Result<()> {
Ok(std::fs::create_dir_all(self.shard_path(shard))?)
}

pub fn state_file_path(&self, shard: &ShardIdentifier, state_id: StateId) -> PathBuf {
self.shard_path(shard).join(to_file_name(state_id))
}

pub fn file_for_state_exists(&self, shard: &ShardIdentifier, state_id: StateId) -> bool {
self.state_file_path(shard, state_id).exists()
}

#[cfg(feature = "test")]
pub fn given_initialized_shard(&self, shard: &ShardIdentifier) {
if self.shard_exists(shard) {
self.purge_shard_dir(shard);
}
self.create_shard(&shard).unwrap()
}
}

/// Trait to abstract file I/O for state.
pub trait StateFileIo {
Expand Down Expand Up @@ -91,10 +165,8 @@ pub trait StateFileIo {

#[cfg(feature = "sgx")]
pub mod sgx {

use super::*;
use crate::error::Error;
use base58::FromBase58;
use codec::Decode;
use core::fmt::Debug;
use itp_hashing::Hash;
Expand All @@ -108,6 +180,7 @@ pub mod sgx {
/// SGX state file I/O.
pub struct SgxStateFileIo<StateKeyRepository, State> {
state_key_repository: Arc<StateKeyRepository>,
state_dir: StateDir,
_phantom: PhantomData<State>,
}

Expand All @@ -117,8 +190,8 @@ pub mod sgx {
<StateKeyRepository as AccessKey>::KeyType: StateCrypto,
State: SgxExternalitiesTrait,
{
pub fn new(state_key_repository: Arc<StateKeyRepository>) -> Self {
SgxStateFileIo { state_key_repository, _phantom: PhantomData }
pub fn new(state_key_repository: Arc<StateKeyRepository>, state_dir: StateDir) -> Self {
SgxStateFileIo { state_key_repository, state_dir, _phantom: PhantomData }
}

fn read(&self, path: &Path) -> Result<Vec<u8>> {
Expand Down Expand Up @@ -163,11 +236,11 @@ pub mod sgx {
shard_identifier: &ShardIdentifier,
state_id: StateId,
) -> Result<Self::StateType> {
if !file_for_state_exists(shard_identifier, state_id) {
if !self.state_dir.file_for_state_exists(shard_identifier, state_id) {
return Err(Error::InvalidStateId(state_id))
}

let state_path = state_file_path(shard_identifier, state_id);
let state_path = self.state_dir.state_file_path(shard_identifier, state_id);
trace!("loading state from: {:?}", state_path);
let state_encoded = self.read(&state_path)?;

Expand Down Expand Up @@ -203,7 +276,7 @@ pub mod sgx {
state_id: StateId,
state: &Self::StateType,
) -> Result<Self::HashType> {
init_shard(&shard_identifier)?;
self.state_dir.create_shard(&shard_identifier)?;
self.write(shard_identifier, state_id, state)
}

Expand All @@ -215,7 +288,7 @@ pub mod sgx {
state_id: StateId,
state: &Self::StateType,
) -> Result<Self::HashType> {
let state_path = state_file_path(shard_identifier, state_id);
let state_path = self.state_dir.state_file_path(shard_identifier, state_id);
trace!("writing state to: {:?}", state_path);

// Only save the state, the state diff is pruned.
Expand All @@ -229,114 +302,79 @@ pub mod sgx {
}

fn remove(&self, shard_identifier: &ShardIdentifier, state_id: StateId) -> Result<()> {
fs::remove_file(state_file_path(shard_identifier, state_id))
.map_err(|e| Error::Other(e.into()))
Ok(fs::remove_file(self.state_dir.state_file_path(shard_identifier, state_id))?)
}

fn shard_exists(&self, shard_identifier: &ShardIdentifier) -> bool {
shard_exists(shard_identifier)
self.state_dir.shard_exists(shard_identifier)
}

fn list_shards(&self) -> Result<Vec<ShardIdentifier>> {
list_shards()
self.state_dir.list_shards()
}

fn list_state_ids_for_shard(
&self,
shard_identifier: &ShardIdentifier,
) -> Result<Vec<StateId>> {
let shard_path = shard_path(shard_identifier);
let directory_items = list_items_in_directory(&shard_path);

Ok(directory_items
.iter()
.flat_map(|item| {
let maybe_state_id = extract_state_id_from_file_name(item.as_str());
if maybe_state_id.is_none() {
warn!("Found item ({}) that does not match state snapshot naming pattern, ignoring it", item)
}
maybe_state_id
})
.collect())
fn list_state_ids_for_shard(&self, shard: &ShardIdentifier) -> Result<Vec<StateId>> {
self.state_dir.list_state_ids_for_shard(shard)
}
}
}

fn state_file_path(shard: &ShardIdentifier, state_id: StateId) -> PathBuf {
let mut shard_file_path = shard_path(shard);
shard_file_path.push(to_file_name(state_id));
shard_file_path
}

fn file_for_state_exists(shard: &ShardIdentifier, state_id: StateId) -> bool {
state_file_path(shard, state_id).exists()
}

/// Returns true if a shard directory for a given identifier exists AND contains at least one state file.
pub(crate) fn shard_exists(shard: &ShardIdentifier) -> bool {
let shard_path = shard_path(shard);
if !shard_path.exists() {
return false
/// Lists all files with a valid state snapshot naming pattern.
pub(crate) fn state_ids_for_shard(shard_path: &Path) -> Result<impl Iterator<Item = StateId>> {
Ok(items_in_directory(shard_path)?.filter_map(|item| {
match extract_state_id_from_file_name(&item) {
Some(state_id) => Some(state_id),
None => {
log::warn!(
"Found item ({}) that does not match state snapshot naming pattern, ignoring it",
item
);
None
},
}
}))
}

shard_path
.read_dir()
// When the iterator over all files in the directory returns none, the directory is empty.
.map(|mut d| d.next().is_some())
.unwrap_or(false)
}

pub(crate) fn init_shard(shard: &ShardIdentifier) -> Result<()> {
let path = shard_path(shard);
fs::create_dir_all(path).map_err(|e| Error::Other(e.into()))
}

/// List any valid shards that are found in the shard path.
/// Ignore any items (files, directories) that are not valid shard identifiers.
pub(crate) fn list_shards() -> Result<Vec<ShardIdentifier>> {
let directory_items = list_items_in_directory(&PathBuf::from(format!("./{}", SHARDS_PATH)));
Ok(directory_items
.iter()
.flat_map(|item| {
item.from_base58()
.ok()
.map(|encoded_shard_id| {
ShardIdentifier::decode(&mut encoded_shard_id.as_slice()).ok()
})
.flatten()
})
.collect())
}

fn list_items_in_directory(directory: &Path) -> Vec<String> {
let items = match directory.read_dir() {
Ok(rd) => rd,
Err(_) => return Vec::new(),
};
/// Returns an iterator over all valid shards in a directory.
///
/// Ignore any items (files, directories) that are not valid shard identifiers.
pub(crate) fn list_shards(path: &Path) -> Result<impl Iterator<Item = ShardIdentifier>> {
Ok(items_in_directory(path)?.filter_map(|base58| match shard_from_base58(&base58) {
Ok(shard) => Some(shard),
Err(e) => {
error!("Found invalid shard ({}). Error: {:?}", base58, e);
None
},
}))
}

items
.flat_map(|fr| fr.map(|de| de.file_name().into_string().ok()).ok().flatten())
.collect()
}
fn shard_from_base58(base58: &str) -> Result<ShardIdentifier> {
let vec = base58.from_base58()?;
Ok(Decode::decode(&mut vec.as_slice())?)
}

/// Remove a shard directory with all of its content.
pub fn purge_shard_dir(shard: &ShardIdentifier) {
let shard_dir_path = shard_path(shard);
if let Err(e) = std::fs::remove_dir_all(&shard_dir_path) {
error!("Failed to remove shard directory {:?}: {:?}", shard_dir_path, e);
}
/// Returns an iterator over all filenames in a directory.
fn items_in_directory(directory: &Path) -> Result<impl Iterator<Item = String>> {
Ok(directory
.read_dir()?
.filter_map(|fr| fr.ok().and_then(|de| de.file_name().into_string().ok())))
}

pub(crate) fn shard_path(shard: &ShardIdentifier) -> PathBuf {
PathBuf::from(format!("./{}/{}", SHARDS_PATH, shard.encode().to_base58()))
fn shard_contains_valid_state_id(path: &Path) -> bool {
// If at least on item can be decoded into a state id, the shard is not empty.
match state_ids_for_shard(path) {
Ok(mut iter) => iter.next().is_some(),
Err(e) => {
error!("Error in reading shard dir: {:?}", e);
false
},
}
}

#[cfg(any(test, feature = "sgx"))]
fn to_file_name(state_id: StateId) -> String {
format!("{}_{}", state_id, ENCRYPTED_STATE_FILE)
}

#[cfg(any(test, feature = "sgx"))]
fn extract_state_id_from_file_name(file_name: &str) -> Option<StateId> {
let state_id_str = file_name.strip_suffix(format!("_{}", ENCRYPTED_STATE_FILE).as_str())?;
state_id_str.parse::<StateId>().ok()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,14 @@ fn sgx_externalities_wrapper() -> ExternalStateGenerator<SgxExternalitiesType, S
#[cfg(feature = "sgx")]
pub mod sgx {
use super::*;
use crate::file_io::sgx::list_shards;
use crate::file_io::list_shards;
use std::path::Path;

pub fn create_in_memory_state_io_from_shards_directories(
path: &Path,
) -> Result<Arc<InMemoryStateFileIo<SgxExternalitiesType, SgxExternalities>>> {
let shards = list_shards()?;
let shards: Vec<ShardIdentifier> =
list_shards(path).map(|iter| iter.collect()).unwrap_or_default();
Ok(create_in_memory_externalities_state_io(&shards))
}
}
Expand Down
Loading