Skip to content

Commit ee2eb13

Browse files
authored
Implement Ipfs read and write with verification in enclave
this implements gitcoin bounty #70 Co-authored-by: bwty <[email protected]>
1 parent 3ac405b commit ee2eb13

File tree

10 files changed

+437
-71
lines changed

10 files changed

+437
-71
lines changed

enclave/Cargo.lock

Lines changed: 231 additions & 12 deletions
Large diffs are not rendered by default.

enclave/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ itertools = { version = "0.8", default-features = false, features = []}
5050
bit-vec = { version = "0.6", default-features = false }
5151
base58 = { rev = "sgx_1.1.2", package="rust-base58", git = "https://github.com/mesalock-linux/rust-base58-sgx", default-features = false, features=["mesalock_sgx"] }
5252

53+
cid = { git = "https://github.com/whalelephant/rust-cid", branch = "nstd", default-features = false }
54+
multibase = { git = "https://github.com/whalelephant/rust-multibase", branch = "nstd", default-features = false }
55+
56+
5357
[dependencies.webpki]
5458
git = "https://github.com/mesalock-linux/webpki"
5559
branch = "mesalock_sgx"
@@ -125,3 +129,10 @@ features = ["sgx"]
125129

126130
[patch.crates-io]
127131
sp-io = { git = "https://github.com/scs/sgx-runtime", default-features = false, features = ["disable_oom", "disable_panic_handler", "disable_allocator", "sgx"]}
132+
133+
[dependencies.ipfs-unixfs]
134+
git = "https://github.com/whalelephant/rust-ipfs"
135+
branch = "w-nstd"
136+
default-features = false
137+
138+

enclave/Enclave.edl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ enclave {
9898
);
9999

100100
sgx_status_t ocall_read_ipfs(
101-
[out, size = state_size] uint8_t * enc_state, uint32_t state_size,
102101
[in, size = cid_size] uint8_t * cid, uint32_t cid_size
103102
);
104103

enclave/src/ipfs.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
use cid::{Cid, Result as CidResult};
2+
use ipfs_unixfs::file::adder::FileAdder;
3+
use log::*;
4+
use multibase::Base;
5+
use std::convert::TryFrom;
6+
use std::vec::Vec;
7+
8+
pub struct IpfsContent {
9+
pub cid: CidResult<Cid>,
10+
pub file_content: Vec<u8>,
11+
pub stats: Stats,
12+
}
13+
#[derive(Debug, PartialEq)]
14+
pub enum IpfsError {
15+
InputCidInvalid,
16+
FinalCidMissing,
17+
Verification,
18+
}
19+
20+
impl IpfsContent {
21+
pub fn new(_cid: &str, _content: Vec<u8>) -> IpfsContent {
22+
IpfsContent {
23+
cid: Cid::try_from(_cid),
24+
file_content: _content,
25+
stats: Stats::default(),
26+
}
27+
}
28+
29+
pub fn verify(&mut self) -> Result<(), IpfsError> {
30+
let mut adder: FileAdder = FileAdder::default();
31+
let mut total: usize = 0;
32+
while total < self.file_content.len() {
33+
let (blocks, consumed) = adder.push(&self.file_content[total..]);
34+
total += consumed;
35+
self.stats.process(blocks);
36+
}
37+
let blocks = adder.finish();
38+
self.stats.process(blocks);
39+
40+
if let Some(last_cid) = self.stats.last.as_ref() {
41+
let cid_str = Base::Base58Btc.encode(last_cid.hash().as_bytes());
42+
info!(
43+
"new cid: {} generated from {} blocks, total of {} bytes",
44+
cid_str, self.stats.blocks, self.stats.block_bytes
45+
);
46+
match self.cid.as_ref() {
47+
Ok(initial_cid) => {
48+
if last_cid.hash().eq(&initial_cid.hash()) {
49+
Ok(())
50+
} else {
51+
Err(IpfsError::Verification)
52+
}
53+
},
54+
Err(_) => {
55+
Err(IpfsError::InputCidInvalid)
56+
}
57+
}
58+
} else {
59+
Err(IpfsError::FinalCidMissing)
60+
}
61+
}
62+
}
63+
#[derive(Default)]
64+
pub struct Stats {
65+
pub blocks: usize,
66+
pub block_bytes: u64,
67+
pub last: Option<Cid>,
68+
}
69+
70+
impl Stats {
71+
fn process<I: Iterator<Item = (Cid, Vec<u8>)>>(&mut self, new_blocks: I) {
72+
for (cid, block) in new_blocks {
73+
self.last = Some(cid);
74+
self.blocks += 1;
75+
self.block_bytes += block.len() as u64;
76+
}
77+
}
78+
}
79+
80+
pub fn test_creates_ipfs_content_struct_works() {
81+
let cid = "QmSaFjwJ2QtS3rZDKzC98XEzv2bqT4TfpWLCpphPPwyQTr";
82+
let content: Vec<u8> = vec![20; 512 * 1024];
83+
let ipfs_content = IpfsContent::new(cid, content.clone());
84+
85+
let cid_str = Base::Base58Btc.encode(ipfs_content.cid.as_ref().unwrap().hash().as_bytes());
86+
assert_eq!(cid_str, cid);
87+
assert_eq!(ipfs_content.file_content, content);
88+
}
89+
90+
pub fn test_verification_ok_for_correct_content() {
91+
let cid = "QmSaFjwJ2QtS3rZDKzC98XEzv2bqT4TfpWLCpphPPwyQTr";
92+
let content: Vec<u8> = vec![20; 512 * 1024];
93+
let mut ipfs_content = IpfsContent::new(cid, content);
94+
let verification = ipfs_content.verify();
95+
assert_eq!(verification.is_ok(), true);
96+
}
97+
98+
pub fn test_verification_fails_for_incorrect_content() {
99+
let cid = "QmSaFjwJ2QtS3rZDKzC98XEzv2bqT4TfpWLCpphPPwyQTr";
100+
let content: Vec<u8> = vec![10; 512 * 1024];
101+
let mut ipfs_content = IpfsContent::new(cid, content);
102+
let verification = ipfs_content.verify();
103+
assert_eq!(verification.unwrap_err(), IpfsError::Verification);
104+
}

enclave/src/lib.rs

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ use std::slice;
5050
use std::string::String;
5151
use std::vec::Vec;
5252

53+
use ipfs::IpfsContent;
5354
use std::collections::HashMap;
55+
use std::fs::File;
56+
use std::io::Read;
5457
use utils::write_slice_and_whitespace_pad;
5558

5659
use crate::constants::{CALL_WORKER, SHIELD_FUNDS};
@@ -62,13 +65,14 @@ use chain_relay::{
6265
use sp_runtime::OpaqueExtrinsic;
6366
use sp_runtime::{generic::SignedBlock, traits::Header as HeaderT};
6467
use substrate_api_client::extrinsic::xt_primitives::UncheckedExtrinsicV4;
65-
use substratee_stf::sgx::{OpaqueCall, shards_key_hash, storage_hashes_to_update_per_shard};
68+
use substratee_stf::sgx::{shards_key_hash, storage_hashes_to_update_per_shard, OpaqueCall};
6669

6770
mod aes;
6871
mod attestation;
6972
mod constants;
7073
mod ed25519;
7174
mod io;
75+
mod ipfs;
7276
mod rsa3072;
7377
mod state;
7478
mod utils;
@@ -227,7 +231,7 @@ pub unsafe extern "C" fn get_state(
227231

228232
let mut state = match state::load(&shard) {
229233
Ok(s) => s,
230-
Err(status) => return status
234+
Err(status) => return status,
231235
};
232236

233237
let validator = match io::light_validation::unseal() {
@@ -363,7 +367,6 @@ pub unsafe extern "C" fn sync_chain_relay(
363367
Ok(c) => calls.extend(c.into_iter()),
364368
Err(_) => error!("Error executing relevant extrinsics"),
365369
};
366-
367370
}
368371

369372
if let Err(_e) = stf_post_actions(validator, calls, xt_slice, *nonce) {
@@ -391,7 +394,8 @@ pub fn update_states(header: Header) -> SgxResult<()> {
391394
if let Some(maybe_shards) = update_map.get(&shards_key_hash()) {
392395
match maybe_shards {
393396
Some(shards) => {
394-
let shards: Vec<ShardIdentifier> = Decode::decode(&mut shards.as_slice()).sgx_error_with_log("error decoding shards")?;
397+
let shards: Vec<ShardIdentifier> = Decode::decode(&mut shards.as_slice())
398+
.sgx_error_with_log("error decoding shards")?;
395399
for s in shards {
396400
if !state::exists(&s) {
397401
info!("Initialized new shard that was found on chain: {:?}", s);
@@ -403,7 +407,8 @@ pub fn update_states(header: Header) -> SgxResult<()> {
403407
.map(|key| WorkerRequest::ChainStorage(key, Some(header.hash())))
404408
.collect();
405409

406-
let responses: Vec<WorkerResponse<Vec<u8>>> = worker_request(per_shard_request)?;
410+
let responses: Vec<WorkerResponse<Vec<u8>>> =
411+
worker_request(per_shard_request)?;
407412
let per_shard_update_map = verify_worker_responses(responses, header.clone())?;
408413

409414
let mut state = state::load(&s)?;
@@ -412,7 +417,7 @@ pub fn update_states(header: Header) -> SgxResult<()> {
412417
state::write(state, &s)?;
413418
}
414419
}
415-
None => info!("No shards are on the chain yet")
420+
None => info!("No shards are on the chain yet"),
416421
};
417422
};
418423
Ok(())
@@ -596,8 +601,6 @@ fn verify_worker_responses(
596601
extern "C" {
597602
pub fn ocall_read_ipfs(
598603
ret_val: *mut sgx_status_t,
599-
enc_state: *mut u8,
600-
enc_state_size: u32,
601604
cid: *const u8,
602605
cid_size: u32,
603606
) -> sgx_status_t;
@@ -629,6 +632,9 @@ extern "C" {
629632
pub extern "C" fn test_main_entrance() -> size_t {
630633
rsgx_unit_tests!(
631634
state::test_encrypted_state_io_works,
635+
ipfs::test_creates_ipfs_content_struct_works,
636+
ipfs::test_verification_ok_for_correct_content,
637+
ipfs::test_verification_fails_for_incorrect_content,
632638
test_ocall_read_write_ipfs,
633639
test_ocall_worker_request
634640
)
@@ -638,7 +644,7 @@ fn test_ocall_read_write_ipfs() {
638644
info!("testing IPFS read/write. Hopefully ipfs daemon is running...");
639645
let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED;
640646
let mut cid_buf: Vec<u8> = vec![0; 46];
641-
let enc_state: Vec<u8> = vec![20; 36];
647+
let enc_state: Vec<u8> = vec![20; 4 * 512 * 1024];
642648

643649
let _res = unsafe {
644650
ocall_write_ipfs(
@@ -650,18 +656,28 @@ fn test_ocall_read_write_ipfs() {
650656
)
651657
};
652658

653-
let mut ret_state = vec![0; 36];
654-
let _res = unsafe {
659+
let res = unsafe {
655660
ocall_read_ipfs(
656661
&mut rt as *mut sgx_status_t,
657-
ret_state.as_mut_ptr(),
658-
ret_state.len() as u32,
659662
cid_buf.as_ptr(),
660663
cid_buf.len() as u32,
661664
)
662665
};
663666

664-
assert_eq!(enc_state, ret_state);
667+
if res == sgx_status_t::SGX_SUCCESS {
668+
let cid = std::str::from_utf8(&cid_buf).unwrap();
669+
let mut f = File::open(&cid).unwrap();
670+
let mut content_buf = Vec::new();
671+
f.read_to_end(&mut content_buf).unwrap();
672+
info!("reading file {:?} of size {} bytes", f, &content_buf.len());
673+
674+
let mut ipfs_content = IpfsContent::new(cid, content_buf);
675+
let verification = ipfs_content.verify();
676+
assert_eq!(verification.is_ok(), true);
677+
} else {
678+
error!("was not able to write to file");
679+
assert!(false);
680+
}
665681
}
666682

667683
// TODO: this is redundantly defined in worker/src/main.rs

enclave/src/state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
*/
1717

1818
use std::fs;
19-
use std::vec::Vec;
2019
use std::io::Write;
20+
use std::vec::Vec;
2121

2222
use log::*;
2323
use sgx_tcrypto::rsgx_sha256_slice;

stf/src/sgx.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use sp_io::SgxExternalitiesTrait;
1212
use sp_runtime::traits::Dispatchable;
1313

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

@@ -71,14 +71,12 @@ impl Stf {
7171

7272
pub fn update_storage(ext: &mut State, map_update: &HashMap<Vec<u8>, Option<Vec<u8>>>) {
7373
ext.execute_with(|| {
74-
map_update
75-
.iter()
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-
});
74+
map_update.iter().for_each(|(k, v)| {
75+
match v {
76+
Some(value) => sp_io::storage::set(k, value),
77+
None => sp_io::storage::clear(k),
78+
};
79+
});
8280
});
8381
}
8482

@@ -210,7 +208,10 @@ impl Stf {
210208
}
211209

212210
pub fn get_storage_hashes_to_update_for_getter(getter: &TrustedGetterSigned) -> Vec<Vec<u8>> {
213-
info!("No specific storage updates needed for getter. Returning those for on block: {:?}", getter.getter);
211+
info!(
212+
"No specific storage updates needed for getter. Returning those for on block: {:?}",
213+
getter.getter
214+
);
214215
Self::storage_hashes_to_update_on_block()
215216
}
216217

0 commit comments

Comments
 (0)