Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 17 additions & 15 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2019,7 +2019,7 @@ dependencies = [
"integritee-node-runtime",
"ita-stf",
"itc-rpc-client",
"itp-api-client-extensions",
"itp-node-api-extensions",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

After discussing with @haerdib, I decided to rename the crate and move the node api factory into this crate (so it can be re-used)

"itp-types",
"json",
"log 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)",
Expand Down Expand Up @@ -2105,12 +2105,13 @@ dependencies = [
"ita-stf",
"itc-rpc-client",
"itc-rpc-server",
"itp-api-client-extensions",
"itp-enclave-api",
"itp-node-api-extensions",
"itp-settings",
"itp-test",
"itp-types",
"its-consensus-slots",
"its-peer-fetch",
"its-primitives",
"its-storage",
"its-test",
Expand Down Expand Up @@ -2445,18 +2446,6 @@ version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"

[[package]]
name = "itp-api-client-extensions"
version = "0.8.0"
dependencies = [
"itp-types",
"parity-scale-codec",
"sp-core",
"sp-finality-grandpa",
"sp-runtime",
"substrate-api-client",
]

[[package]]
name = "itp-component-container"
version = "0.8.0"
Expand Down Expand Up @@ -2509,6 +2498,19 @@ dependencies = [
"thiserror 1.0.9",
]

[[package]]
name = "itp-node-api-extensions"
version = "0.8.0"
dependencies = [
"itp-types",
"parity-scale-codec",
"sp-core",
"sp-finality-grandpa",
"sp-runtime",
"substrate-api-client",
"thiserror 1.0.30",
]

[[package]]
name = "itp-nonce-cache"
version = "0.8.0"
Expand Down Expand Up @@ -2826,7 +2828,7 @@ dependencies = [
"anyhow",
"async-trait",
"itc-rpc-client",
"itp-api-client-extensions",
"itp-node-api-extensions",
"itp-test",
"its-primitives",
"its-storage",
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ members = [
"core/rpc-client",
"core/rpc-server",
"core/tls-websocket-server",
"core-primitives/api-client-extensions",
"core-primitives/node-api-extensions",
"core-primitives/component-container",
"core-primitives/enclave-api",
"core-primitives/enclave-api/ffi",
Expand Down
2 changes: 1 addition & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ sp-core = { version = "4.1.0-dev", git = "https://github.com/paritytech/substrat

#local dependencies
itp-types = { path = "../core-primitives/types" }
itp-api-client-extensions = { path = "../core-primitives/api-client-extensions" }
itp-node-api-extensions = { path = "../core-primitives/node-api-extensions" }
ita-stf = { path = "../app-libs/stf" }
itc-rpc-client = { path = "../core/rpc-client" }
2 changes: 1 addition & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ use teerex_primitives::Request;

use ita_stf::{ShardIdentifier, TrustedCallSigned, TrustedOperation};
use itc_rpc_client::direct_client::{DirectApi, DirectClient as DirectWorkerApi};
use itp_api_client_extensions::{PalletTeerexApi, TEEREX};
use itp_node_api_extensions::{PalletTeerexApi, TEEREX};
use itp_types::{DirectRequestStatus, RpcRequest, RpcResponse, RpcReturnValue};
use substrate_client_keystore::{KeystoreExt, LocalKeystore};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
[package]
name = "itp-api-client-extensions"
name = "itp-node-api-extensions"
version = "0.8.0"
authors = ["Integritee AG <[email protected]>"]
edition = "2018"

[dependencies]
# crates.io
codec = { package = "parity-scale-codec", version = "2.0.0", features = ["derive"] }
thiserror = "1.0"

# substrate
sp-core = { version = "4.1.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "master" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub use substrate_api_client::ApiClientError;

pub mod account;
pub mod chain;
pub mod node_api_factory;
pub mod pallet_teerex;

#[cfg(feature = "mocks")]
Expand Down
56 changes: 56 additions & 0 deletions core-primitives/node-api-extensions/src/node_api_factory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
Copyright 2021 Integritee AG and Supercomputing Systems AG
Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved.
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.
*/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Move the NodeApiFactory here, so it can be re-used from the sidechain crate (was in the service crate before)

use sp_core::sr25519;
use substrate_api_client::{rpc::WsRpcClient, Api};

/// Trait to create a node API, based on a node URL and signer.
pub trait CreateNodeApi {
fn create_api(&self) -> Result<Api<sr25519::Pair, WsRpcClient>>;
}

/// Node API factory error.
#[derive(Debug, thiserror::Error)]
pub enum NodeApiFactoryError {
#[error("Failed to create a node API: {0}")]
FailedToCreateNodeApi(#[from] substrate_api_client::ApiClientError),
#[error(transparent)]
Other(#[from] Box<dyn std::error::Error + Sync + Send + 'static>),
}

pub type Result<T> = std::result::Result<T, NodeApiFactoryError>;

/// Node API factory implementation.
pub struct NodeApiFactory {
node_url: String,
signer: sr25519::Pair,
}

impl NodeApiFactory {
pub fn new(url: String, signer: sr25519::Pair) -> Self {
NodeApiFactory { node_url: url, signer }
}
}

impl CreateNodeApi for NodeApiFactory {
fn create_api(&self) -> Result<Api<sr25519::Pair, WsRpcClient>> {
Api::<sr25519::Pair, WsRpcClient>::new(WsRpcClient::new(self.node_url.as_str()))
.map_err(NodeApiFactoryError::FailedToCreateNodeApi)
.map(|a| a.set_signer(self.signer.clone()))
}
}
11 changes: 10 additions & 1 deletion core-primitives/ocall-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ pub extern crate alloc;

use alloc::vec::Vec;
use codec::{Decode, Encode};
use itp_types::{TrustedOperationStatus, WorkerRequest, WorkerResponse};
use itp_types::{
BlockHash, ShardIdentifier, TrustedOperationStatus, WorkerRequest, WorkerResponse,
};
use sgx_types::*;
use sp_runtime::OpaqueExtrinsic;

Expand Down Expand Up @@ -75,10 +77,17 @@ pub trait EnclaveSidechainOCallApi: Clone + Send + Sync {
&self,
signed_blocks: Vec<SignedSidechainBlock>,
) -> SgxResult<()>;

fn store_sidechain_blocks<SignedSidechainBlock: Encode>(
&self,
signed_blocks: Vec<SignedSidechainBlock>,
) -> SgxResult<()>;

fn fetch_sidechain_blocks_from_peer<SignedSidechainBlock: Decode>(
&self,
last_known_block_hash: BlockHash,
shard_identifier: ShardIdentifier,
) -> SgxResult<Vec<SignedSidechainBlock>>;
Comment on lines +86 to +90
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Extend the o-call API on the enclave side

}

/// Newtype for IPFS CID
Expand Down
10 changes: 9 additions & 1 deletion core-primitives/test/src/mock/onchain_mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use itp_ocall_api::{EnclaveAttestationOCallApi, EnclaveSidechainOCallApi};
use itp_storage::StorageEntryVerified;
use itp_storage_verifier::{GetStorageVerified, Result};
use itp_teerex_storage::{TeeRexStorage, TeerexStorageKeys};
use itp_types::Enclave;
use itp_types::{BlockHash, Enclave, ShardIdentifier};
use sgx_types::{
sgx_epid_group_id_t, sgx_measurement_t, sgx_platform_info_t, sgx_quote_nonce_t,
sgx_quote_sign_type_t, sgx_report_t, sgx_spid_t, sgx_target_info_t, sgx_update_info_bit_t,
Expand Down Expand Up @@ -144,6 +144,14 @@ impl EnclaveSidechainOCallApi for OnchainMock {
) -> SgxResult<()> {
Ok(())
}

fn fetch_sidechain_blocks_from_peer<SignedSidechainBlock: Decode>(
&self,
_last_known_block_hash: BlockHash,
_shard_identifier: ShardIdentifier,
) -> SgxResult<Vec<SignedSidechainBlock>> {
Ok(Vec::new())
}
}

// We cannot implement EnclaveOnChainOCallApi specifically here, because OnchainMock already
Expand Down
6 changes: 6 additions & 0 deletions enclave-runtime/Enclave.edl
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ enclave {
[in, size = signed_blocks_size] uint8_t * signed_blocks, uint32_t signed_blocks_size
);

sgx_status_t ocall_fetch_sidechain_blocks_from_peer(
[in, size = last_known_block_hash_size] uint8_t * last_known_block_hash, uint32_t last_known_block_hash_size,
[in, size = shard_identifier_size] uint8_t * shard_identifier, uint32_t shard_identifier_size,
[out, size = sidechain_blocks_size] uint8_t * sidechain_blocks, uint32_t sidechain_blocks_size
);

sgx_status_t ocall_send_to_parentchain(
[in, size = extrinsics_size] uint8_t * extrinsics, uint32_t extrinsics_size
);
Expand Down
10 changes: 10 additions & 0 deletions enclave-runtime/src/ocall/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ extern "C" {
signed_blocks_size: u32,
) -> sgx_status_t;

pub fn ocall_fetch_sidechain_blocks_from_peer(
ret_val: *mut sgx_status_t,
last_known_block_hash: *const u8,
last_known_block_hash_size: u32,
shard_identifier: *const u8,
shard_identifier_size: u32,
sidechain_blocks: *mut u8,
sidechain_blocks_size: u32,
) -> sgx_status_t;

pub fn ocall_send_to_parentchain(
ret_val: *mut sgx_status_t,
extrinsics: *const u8,
Expand Down
40 changes: 39 additions & 1 deletion enclave-runtime/src/ocall/sidechain_ocall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
*/

use crate::ocall::{ffi, OcallApi};
use codec::Encode;
use codec::{Decode, Encode};
use frame_support::ensure;
use itp_ocall_api::EnclaveSidechainOCallApi;
use itp_types::{BlockHash, ShardIdentifier};
use log::*;
use sgx_types::{sgx_status_t, SgxResult};
use std::vec::Vec;

Expand Down Expand Up @@ -65,4 +67,40 @@ impl EnclaveSidechainOCallApi for OcallApi {

Ok(())
}

fn fetch_sidechain_blocks_from_peer<SignedSidechainBlock: Decode>(
&self,
last_known_block_hash: BlockHash,
shard_identifier: ShardIdentifier,
) -> SgxResult<Vec<SignedSidechainBlock>> {
let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED;
let last_known_block_hash_encoded = last_known_block_hash.encode();
let shard_identifier_encoded = shard_identifier.encode();

// We have to pre-allocate the vector and hope it's large enough
let mut signed_blocks_encoded: Vec<u8> = vec![0; 4096 * 16];
Comment on lines +80 to +81
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 is what worries me a bit: In order to get the sidechain blocks over the o-call interface, we have to pre-allocate the vector into which the block data is filled. I think there's no other elegant solution to this? An alternative would be to have an additional o-call to query the size first, so we can then allocate exactly the right amount. But it would basically double the number of calls into our block storage.

Copy link
Contributor

@haerdib haerdib Jan 14, 2022

Choose a reason for hiding this comment

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

Ouf, you're right. That's quite ugly. I do see a different solution, but that might require some redesigning:

Instead of fetching the blocks directly with this ocall, you could start a fetcher function on the untrusted side with it. This function then retrieves the sidechain blocks and buffers them on the untrusted side (not sure if buffering is needed, there might be a simpler solution). For each retrieved sidechain block (or a chunk of sidechain blocks), it would then do an ecall to import the block (I guess that could be the same ecall as for the gossiped sidechain blocks).

Copy link
Contributor

Choose a reason for hiding this comment

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

The block production suspension would then also need to be lifted via ecall.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm I think that would be more complex to do, because we would go from a pull model to a push model (from the enclave perspective), which is not easy to do in the current PeerBlockSyncer.
An alternative would be to extend the o-call to have an additional output parameter, which is the total buffer size required. And this will be set in case the default one is not enough. We can then detect this in our o-call and adjust our buffer size accordingly and make the call again.

Copy link
Contributor

@haerdib haerdib Jan 17, 2022

Choose a reason for hiding this comment

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

I do agree that this is more complex. But simply adjusting the buffer to an arbitrary amount, I also don't think is sensible - except if we limit our sidechain buffering to a reasonable size which we can import in one go.
Either way - I think this issue is worth its own PR and discussion, so I suggest opening a new issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Issue created -> #621


let res = unsafe {
ffi::ocall_fetch_sidechain_blocks_from_peer(
&mut rt as *mut sgx_status_t,
last_known_block_hash_encoded.as_ptr(),
last_known_block_hash_encoded.len() as u32,
shard_identifier_encoded.as_ptr(),
shard_identifier_encoded.len() as u32,
signed_blocks_encoded.as_mut_ptr(),
signed_blocks_encoded.len() as u32,
)
};

ensure!(rt == sgx_status_t::SGX_SUCCESS, rt);
ensure!(res == sgx_status_t::SGX_SUCCESS, res);

let decoded_signed_blocks: Vec<SignedSidechainBlock> =
Decode::decode(&mut signed_blocks_encoded.as_slice()).map_err(|e| {
error!("Failed to decode WorkerResponse: {}", e);
sgx_status_t::SGX_ERROR_UNEXPECTED
})?;

Ok(decoded_signed_blocks)
}
}
12 changes: 11 additions & 1 deletion enclave-runtime/src/test/mocks/propose_to_import_call_mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
use crate::test::mocks::types::TestBlockImporter;
use codec::{Decode, Encode};
use itp_ocall_api::{EnclaveOnChainOCallApi, EnclaveSidechainOCallApi};
use itp_types::{Header as ParentchainHeader, WorkerRequest, WorkerResponse};
use itp_types::{
BlockHash, Header as ParentchainHeader, ShardIdentifier, WorkerRequest, WorkerResponse,
};
use its_sidechain::{
consensus_common::BlockImport, primitives::types::SignedBlock as SignedSidechainBlockType,
};
Expand Down Expand Up @@ -82,4 +84,12 @@ impl EnclaveSidechainOCallApi for ProposeToImportOCallApi {
) -> SgxResult<()> {
Ok(())
}

fn fetch_sidechain_blocks_from_peer<SignedSidechainBlock: Decode>(
&self,
_last_known_block_hash: BlockHash,
_shard_identifier: ShardIdentifier,
) -> SgxResult<Vec<SignedSidechainBlock>> {
Ok(Vec::new())
}
}
8 changes: 6 additions & 2 deletions service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ sgx_crypto_helper = { branch = "master", git = "https://github.com/apache/teacla
ita-stf = { path = "../app-libs/stf" }
itc-rpc-client = { path = "../core/rpc-client" }
itc-rpc-server = { path = "../core/rpc-server" }
itp-api-client-extensions = { path = "../core-primitives/api-client-extensions" }
itp-node-api-extensions = { path = "../core-primitives/node-api-extensions" }
itp-enclave-api = { path = "../core-primitives/enclave-api" }
itp-settings = { path = "../core-primitives/settings" }
itp-test = { path = "../core-primitives/test" }
itp-types = { path = "../core-primitives/types" }
its-consensus-slots = { path = "../sidechain/consensus/slots" }
its-peer-fetch = { path = "../sidechain/peer-fetch" }
its-primitives = { path = "../sidechain/primitives"}
its-storage = { path = "../sidechain/storage" }

Expand All @@ -72,6 +73,9 @@ default = []
production = ['itp-settings/production']

[dev-dependencies]
# crates.io
anyhow = "1.0.40"
its-test = { path = "../sidechain/test" }
mockall = "0.10.1"
# local
its-peer-fetch = { path = "../sidechain/peer-fetch", features = ["mocks"] }
its-test = { path = "../sidechain/test" }
2 changes: 2 additions & 0 deletions service/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pub enum Error {
ApplicationSetup,
#[error("Retrieved empty value")]
EmptyValue,
#[error("Insufficient buffer size")]
InsufficientBufferSize,
#[error("Custom Error: {0}")]
Custom(Box<dyn std::error::Error + Sync + Send + 'static>),
}
Loading