From 4689acfd4faabc5891345daa21ce7669c358eab3 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 15 Nov 2023 13:06:49 +0100 Subject: [PATCH] Shielding parentchain transfers (#1378) * Adding in the balance transfer filters * clippy and fmt * taplo * adding polkadot js transfer script for various transfer tests and some logging * cleanup for pr * adding privacy sidechain shielding to indirect executor * Adding in privacy shielding with runtime crash * fix merge error * [stf] migrate deprecated `set_balance` to `force_set_balance` * renaming and refactor * reorganizing and refactor for better code seperation * bump api-client after master merge * cargo fmt * making indirect_executor refactored to avoid cycles * make itp-utils purely no-std * make itp-utils purely no-std * code fixes after itp-utils change * fix enclave build * [itp-utils] fix test compilation * Fix enclave build in shielding parentchain transfer branch (#1482) * make itp-utils purely no-std * make itp-utils purely no-std * code fixes after itp-utils change * fix enclave build * [itp-utils] fix test compilation * adding integration test * clippy taplo fmt * avoid clash between //Alice unshielding and //Alice vault for shielding. * WIP causing cyclic dependency * plenty of refactoring. now no_std clash remaining * mostly fixed the mess. now failing high-level when build * mock for indirect call executor tests * fixed cargo test * cleanups * fixes * cargo fix in root * taplo fmt * toml feature cleanup. no effect * revert bogus fix * taplo fmt * fix trait bound build errors * cleanup * get rid of extern crate core again * get rid of sgx_tstd in itp-types * add enhancement todo comment --------- Co-authored-by: Christian Langenbacher Co-authored-by: clangenb <37865735+clangenb@users.noreply.github.com> Co-authored-by: Alain Brenzikofer Co-authored-by: brenzi --- Cargo.lock | 41 ++- Cargo.toml | 1 + app-libs/parentchain-interface/Cargo.toml | 93 +++++++ .../src/indirect_calls/invoke.rs | 11 +- .../src/indirect_calls/mod.rs | 6 +- .../src/indirect_calls/shield_funds.rs | 14 +- .../transfer_to_alice_shields_funds.rs | 19 +- .../src/integritee/event_filter.rs | 90 +++++++ .../src/integritee/event_handler.rs | 80 ++++++ .../src/integritee/extrinsic_parser.rs | 10 +- .../src/integritee/mod.rs | 108 ++++++++ app-libs/parentchain-interface/src/lib.rs | 38 +++ .../src/target_a/event_filter.rs | 89 +++++++ .../src/target_a/event_handler.rs | 37 +++ .../src/target_a/extrinsic_parser.rs | 77 ++++++ .../parentchain-interface/src/target_a/mod.rs | 112 ++++++++ .../src/target_b/event_filter.rs | 89 +++++++ .../src/target_b/event_handler.rs | 37 +++ .../src/target_b/extrinsic_parser.rs | 77 ++++++ .../parentchain-interface/src/target_b/mod.rs | 85 ++++++ app-libs/stf/Cargo.toml | 3 + cli/demo_shielding_unshielding.sh | 4 +- cli/test_auto_shielding_with_transfer_bob.sh | 140 ++++++++++ core-primitives/stf-primitives/Cargo.toml | 1 + core-primitives/stf-primitives/src/lib.rs | 1 + core-primitives/stf-primitives/src/traits.rs | 27 +- core-primitives/test/Cargo.toml | 10 +- core-primitives/types/Cargo.toml | 8 + core-primitives/types/src/lib.rs | 2 - core-primitives/types/src/parentchain.rs | 91 ++++++- core/offchain-worker-executor/Cargo.toml | 1 + core/offchain-worker-executor/src/executor.rs | 2 +- .../indirect-calls-executor/Cargo.toml | 6 +- .../indirect-calls-executor/src/error.rs | 7 + .../src/event_filter.rs | 119 +-------- .../indirect-calls-executor/src/executor.rs | 94 ++++--- .../src/filter_metadata.rs | 191 ++------------ .../indirect-calls-executor/src/lib.rs | 11 +- .../indirect-calls-executor/src/mock.rs | 241 ++++++++++++++++++ .../indirect-calls-executor/src/traits.rs | 34 +-- enclave-runtime/Cargo.lock | 38 ++- enclave-runtime/Cargo.toml | 1 + enclave-runtime/README.md | 2 + .../src/initialization/global_components.rs | 67 ++--- .../src/initialization/parentchain/common.rs | 21 +- enclave-runtime/src/test/top_pool_tests.rs | 10 +- service/src/parentchain_handler.rs | 1 + sidechain/consensus/common/Cargo.toml | 2 - 48 files changed, 1825 insertions(+), 424 deletions(-) create mode 100644 app-libs/parentchain-interface/Cargo.toml rename {core/parentchain/indirect-calls-executor => app-libs/parentchain-interface}/src/indirect_calls/invoke.rs (76%) rename {core/parentchain/indirect-calls-executor => app-libs/parentchain-interface}/src/indirect_calls/mod.rs (90%) rename {core/parentchain/indirect-calls-executor => app-libs/parentchain-interface}/src/indirect_calls/shield_funds.rs (85%) rename {core/parentchain/indirect-calls-executor => app-libs/parentchain-interface}/src/indirect_calls/transfer_to_alice_shields_funds.rs (87%) create mode 100644 app-libs/parentchain-interface/src/integritee/event_filter.rs create mode 100644 app-libs/parentchain-interface/src/integritee/event_handler.rs rename core/parentchain/indirect-calls-executor/src/parentchain_parser.rs => app-libs/parentchain-interface/src/integritee/extrinsic_parser.rs (94%) create mode 100644 app-libs/parentchain-interface/src/integritee/mod.rs create mode 100644 app-libs/parentchain-interface/src/lib.rs create mode 100644 app-libs/parentchain-interface/src/target_a/event_filter.rs create mode 100644 app-libs/parentchain-interface/src/target_a/event_handler.rs create mode 100644 app-libs/parentchain-interface/src/target_a/extrinsic_parser.rs create mode 100644 app-libs/parentchain-interface/src/target_a/mod.rs create mode 100644 app-libs/parentchain-interface/src/target_b/event_filter.rs create mode 100644 app-libs/parentchain-interface/src/target_b/event_handler.rs create mode 100644 app-libs/parentchain-interface/src/target_b/extrinsic_parser.rs create mode 100644 app-libs/parentchain-interface/src/target_b/mod.rs create mode 100644 cli/test_auto_shielding_with_transfer_bob.sh create mode 100644 core/parentchain/indirect-calls-executor/src/mock.rs create mode 100644 enclave-runtime/README.md diff --git a/Cargo.lock b/Cargo.lock index dd1c35e64c..aa42534e7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3002,6 +3002,41 @@ dependencies = [ "url 2.4.0", ] +[[package]] +name = "ita-parentchain-interface" +version = "0.9.0" +dependencies = [ + "binary-merkle-tree", + "bs58", + "env_logger", + "frame-support", + "futures 0.3.28", + "futures 0.3.8", + "ita-sgx-runtime", + "ita-stf", + "itc-parentchain-indirect-calls-executor", + "itc-parentchain-test", + "itp-api-client-types", + "itp-node-api", + "itp-ocall-api", + "itp-sgx-crypto", + "itp-sgx-runtime-primitives", + "itp-stf-executor", + "itp-stf-primitives", + "itp-test", + "itp-top-pool-author", + "itp-types", + "itp-utils", + "log 0.4.19", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-core", + "sp-runtime", + "thiserror 1.0.40", + "thiserror 1.0.9", +] + [[package]] name = "ita-sgx-runtime" version = "0.9.0" @@ -3047,8 +3082,10 @@ dependencies = [ "derive_more", "frame-support", "frame-system", + "hex", "integritee-node-runtime", "ita-sgx-runtime", + "itc-parentchain-indirect-calls-executor", "itp-hashing", "itp-node-api", "itp-node-api-metadata", @@ -3177,7 +3214,6 @@ dependencies = [ "env_logger", "futures 0.3.28", "futures 0.3.8", - "ita-stf", "itc-parentchain-test", "itp-api-client-types", "itp-node-api", @@ -3913,6 +3949,8 @@ dependencies = [ "frame-system", "integritee-node-runtime", "itp-sgx-runtime-primitives", + "itp-stf-primitives", + "itp-utils", "pallet-balances", "parity-scale-codec", "primitive-types", @@ -3921,6 +3959,7 @@ dependencies = [ "sp-core", "sp-runtime", "sp-std", + "substrate-api-client", "teerex-primitives", ] diff --git a/Cargo.toml b/Cargo.toml index efa7c1c0b6..be25a45a99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "app-libs/oracle", + "app-libs/parentchain-interface", "app-libs/sgx-runtime", "app-libs/stf", "cli", diff --git a/app-libs/parentchain-interface/Cargo.toml b/app-libs/parentchain-interface/Cargo.toml new file mode 100644 index 0000000000..ac1042d018 --- /dev/null +++ b/app-libs/parentchain-interface/Cargo.toml @@ -0,0 +1,93 @@ +[package] +name = "ita-parentchain-interface" +version = "0.9.0" +authors = ["Integritee AG "] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +# local dependencies +ita-sgx-runtime = { path = "../sgx-runtime", default-features = false } +ita-stf = { path = "../stf", default-features = false } +itc-parentchain-indirect-calls-executor = { path = "../../core/parentchain/indirect-calls-executor", default-features = false } +itp-api-client-types = { path = "../../core-primitives/node-api/api-client-types", default-features = false } +itp-node-api = { path = "../../core-primitives/node-api", default-features = false } +itp-ocall-api = { path = "../../core-primitives/ocall-api", default-features = false } +itp-sgx-crypto = { path = "../../core-primitives/sgx/crypto", default-features = false } +itp-sgx-runtime-primitives = { path = "../../core-primitives/sgx-runtime-primitives", default-features = false } +itp-stf-executor = { path = "../../core-primitives/stf-executor", default-features = false } +itp-stf-primitives = { path = "../../core-primitives/stf-primitives", default-features = false } +itp-top-pool-author = { path = "../../core-primitives/top-pool-author", default-features = false } +itp-types = { path = "../../core-primitives/types", default-features = false } +itp-utils = { path = "../../core-primitives/utils", default-features = false } + +# sgx enabled external libraries +futures_sgx = { package = "futures", git = "https://github.com/mesalock-linux/futures-rs-sgx", optional = true } +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +futures = { version = "0.3.8", optional = true } +thiserror = { version = "1.0", optional = true } + +# no-std compatible libraries +bs58 = { version = "0.4.0", default-features = false, features = ["alloc"] } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4", default-features = false } + +# substrate dep +binary-merkle-tree = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[dev-dependencies] +env_logger = "0.9.0" +itp-node-api = { path = "../../core-primitives/node-api", features = ["mocks"] } +itp-sgx-crypto = { path = "../../core-primitives/sgx/crypto", features = ["mocks"] } +itp-stf-executor = { path = "../../core-primitives/stf-executor", features = ["mocks"] } +itp-test = { path = "../../core-primitives/test" } +itp-top-pool-author = { path = "../../core-primitives/top-pool-author", features = ["mocks"] } +itc-parentchain-test = { path = "../../core/parentchain/test" } + + +[features] +default = ["std"] +std = [ + "bs58/std", + "codec/std", + "futures", + "ita-sgx-runtime/std", + "ita-stf/std", + "itc-parentchain-indirect-calls-executor/std", + "itp-api-client-types/std", + "itp-node-api/std", + "itp-ocall-api/std", + "itp-sgx-crypto/std", + "itp-sgx-runtime-primitives/std", + "itp-stf-executor/std", + "itp-stf-primitives/std", + "itp-top-pool-author/std", + "itp-types/std", + "itp-utils/std", + "log/std", + #substrate + "binary-merkle-tree/std", + "sp-core/std", + "sp-runtime/std", + "frame-support/std", + "thiserror", +] +sgx = [ + "sgx_tstd", + "futures_sgx", + "ita-stf/sgx", + "itc-parentchain-indirect-calls-executor/sgx", + "itp-node-api/sgx", + "itp-sgx-crypto/sgx", + "itp-stf-executor/sgx", + "itp-top-pool-author/sgx", + "thiserror_sgx", +] diff --git a/core/parentchain/indirect-calls-executor/src/indirect_calls/invoke.rs b/app-libs/parentchain-interface/src/indirect_calls/invoke.rs similarity index 76% rename from core/parentchain/indirect-calls-executor/src/indirect_calls/invoke.rs rename to app-libs/parentchain-interface/src/indirect_calls/invoke.rs index 076e06e87c..ead6ed4d52 100644 --- a/core/parentchain/indirect-calls-executor/src/indirect_calls/invoke.rs +++ b/app-libs/parentchain-interface/src/indirect_calls/invoke.rs @@ -15,8 +15,13 @@ */ -use crate::{error::Result, IndirectDispatch, IndirectExecutor}; use codec::{Decode, Encode}; +use ita_stf::TrustedCallSigned; +use itc_parentchain_indirect_calls_executor::{ + error::{Error, Result}, + IndirectDispatch, +}; +use itp_stf_primitives::traits::IndirectExecutor; use itp_types::Request; #[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] @@ -24,7 +29,9 @@ pub struct InvokeArgs { request: Request, } -impl IndirectDispatch for InvokeArgs { +impl> + IndirectDispatch for InvokeArgs +{ fn dispatch(&self, executor: &Executor) -> Result<()> { log::debug!("Found trusted call extrinsic, submitting it to the top pool"); executor.submit_trusted_call(self.request.shard, self.request.cyphertext.clone()); diff --git a/core/parentchain/indirect-calls-executor/src/indirect_calls/mod.rs b/app-libs/parentchain-interface/src/indirect_calls/mod.rs similarity index 90% rename from core/parentchain/indirect-calls-executor/src/indirect_calls/mod.rs rename to app-libs/parentchain-interface/src/indirect_calls/mod.rs index 4f2da407de..89095a5cf5 100644 --- a/core/parentchain/indirect-calls-executor/src/indirect_calls/mod.rs +++ b/app-libs/parentchain-interface/src/indirect_calls/mod.rs @@ -15,9 +15,9 @@ */ -mod invoke; -mod shield_funds; -mod transfer_to_alice_shields_funds; +pub mod invoke; +pub mod shield_funds; +pub mod transfer_to_alice_shields_funds; pub use invoke::InvokeArgs; pub use shield_funds::ShieldFundsArgs; diff --git a/core/parentchain/indirect-calls-executor/src/indirect_calls/shield_funds.rs b/app-libs/parentchain-interface/src/indirect_calls/shield_funds.rs similarity index 85% rename from core/parentchain/indirect-calls-executor/src/indirect_calls/shield_funds.rs rename to app-libs/parentchain-interface/src/indirect_calls/shield_funds.rs index 1036614da8..507cd93cce 100644 --- a/core/parentchain/indirect-calls-executor/src/indirect_calls/shield_funds.rs +++ b/app-libs/parentchain-interface/src/indirect_calls/shield_funds.rs @@ -15,10 +15,16 @@ */ -use crate::{error::Result, IndirectDispatch, IndirectExecutor}; use codec::{Decode, Encode}; use ita_stf::{Getter, TrustedCall, TrustedCallSigned}; -use itp_stf_primitives::types::{AccountId, TrustedOperation}; +use itc_parentchain_indirect_calls_executor::{ + error::{Error, Result}, + IndirectDispatch, +}; +use itp_stf_primitives::{ + traits::IndirectExecutor, + types::{AccountId, TrustedOperation}, +}; use itp_types::{Balance, ShardIdentifier}; use log::{debug, info}; use std::vec::Vec; @@ -30,7 +36,9 @@ pub struct ShieldFundsArgs { amount: Balance, } -impl IndirectDispatch for ShieldFundsArgs { +impl> + IndirectDispatch for ShieldFundsArgs +{ fn dispatch(&self, executor: &Executor) -> Result<()> { info!("Found ShieldFunds extrinsic in block: \nAccount Encrypted {:?} \nAmount: {} \nShard: {}", self.account_encrypted, self.amount, bs58::encode(self.shard.encode()).into_string()); diff --git a/core/parentchain/indirect-calls-executor/src/indirect_calls/transfer_to_alice_shields_funds.rs b/app-libs/parentchain-interface/src/indirect_calls/transfer_to_alice_shields_funds.rs similarity index 87% rename from core/parentchain/indirect-calls-executor/src/indirect_calls/transfer_to_alice_shields_funds.rs rename to app-libs/parentchain-interface/src/indirect_calls/transfer_to_alice_shields_funds.rs index fa74a29909..a789eca29c 100644 --- a/core/parentchain/indirect-calls-executor/src/indirect_calls/transfer_to_alice_shields_funds.rs +++ b/app-libs/parentchain-interface/src/indirect_calls/transfer_to_alice_shields_funds.rs @@ -15,14 +15,20 @@ */ -use crate::{error::Result, IndirectDispatch, IndirectExecutor}; use codec::{Decode, Encode}; +use core::fmt::Debug; use ita_stf::{Getter, TrustedCall, TrustedCallSigned}; -use itp_stf_primitives::types::{AccountId, TrustedOperation}; +use itc_parentchain_indirect_calls_executor::{ + error::{Error, Result}, + IndirectDispatch, +}; +use itp_stf_primitives::{ + traits::IndirectExecutor, + types::{AccountId, TrustedOperation}, +}; use itp_types::Balance; use log::info; use sp_runtime::MultiAddress; - /// Arguments of a parentchains `transfer` or `transfer_allow_death` dispatchable. /// /// This is a simple demo indirect call where a transfer to alice on chain will transfer @@ -41,8 +47,7 @@ pub struct TransferToAliceShieldsFundsArgs { /// /// ``` /// use sp_core::{sr25519, Pair}; -/// use itc_parentchain_indirect_calls_executor::indirect_calls::ALICE_ACCOUNT_ID; -/// +/// use ita_parentchain_interface::indirect_calls::ALICE_ACCOUNT_ID; /// let alice = sr25519::Pair::from_string_with_seed("//Alice", None).unwrap(); /// println!("{:?}", alice.0.public().to_vec()); /// assert_eq!(ALICE_ACCOUNT_ID, alice.0.public().into()) @@ -52,7 +57,9 @@ pub const ALICE_ACCOUNT_ID: AccountId = AccountId::new([ 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125, ]); -impl IndirectDispatch for TransferToAliceShieldsFundsArgs { +impl> + IndirectDispatch for TransferToAliceShieldsFundsArgs +{ fn dispatch(&self, executor: &Executor) -> Result<()> { if self.destination == ALICE_ACCOUNT_ID.into() { info!("Found Transfer to Alice extrinsic in block: \nAmount: {}", self.value); diff --git a/app-libs/parentchain-interface/src/integritee/event_filter.rs b/app-libs/parentchain-interface/src/integritee/event_filter.rs new file mode 100644 index 0000000000..c9ecb19a85 --- /dev/null +++ b/app-libs/parentchain-interface/src/integritee/event_filter.rs @@ -0,0 +1,90 @@ +/* + Copyright 2021 Integritee 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. + +*/ +//! Various way to filter Parentchain events + +use itc_parentchain_indirect_calls_executor::event_filter::ToEvents; +use itp_api_client_types::Events; + +use itp_types::{ + parentchain::{ + BalanceTransfer, ExtrinsicFailed, ExtrinsicStatus, ExtrinsicSuccess, FilterEvents, + }, + H256, +}; +use std::vec::Vec; + +#[derive(Clone)] +pub struct FilterableEvents(pub Events); + +// todo: improve: https://github.com/integritee-network/worker/pull/1378#discussion_r1393933766 +impl ToEvents> for FilterableEvents { + fn to_events(&self) -> &Events { + &self.0 + } +} + +impl From> for FilterableEvents { + fn from(ev: Events) -> Self { + Self(ev) + } +} + +impl FilterEvents for FilterableEvents { + type Error = itc_parentchain_indirect_calls_executor::Error; + + fn get_extrinsic_statuses(&self) -> core::result::Result, Self::Error> { + Ok(self + .to_events() + .iter() + .filter_map(|ev| { + ev.and_then(|ev| { + if (ev.as_event::()?).is_some() { + return Ok(Some(ExtrinsicStatus::Success)) + } + + if (ev.as_event::()?).is_some() { + return Ok(Some(ExtrinsicStatus::Failed)) + } + + Ok(None) + }) + .ok() + .flatten() + }) + .collect()) + } + + fn get_transfer_events(&self) -> core::result::Result, Self::Error> { + Ok(self + .to_events() + .iter() + .flatten() // flatten filters out the nones + .filter_map(|ev| match ev.as_event::() { + Ok(maybe_event) => { + if maybe_event.is_none() { + log::warn!("Transfer event does not exist in parentchain metadata"); + }; + maybe_event + }, + Err(e) => { + log::error!("Could not decode event: {:?}", e); + None + }, + }) + .collect()) + } +} diff --git a/app-libs/parentchain-interface/src/integritee/event_handler.rs b/app-libs/parentchain-interface/src/integritee/event_handler.rs new file mode 100644 index 0000000000..8e01cbd978 --- /dev/null +++ b/app-libs/parentchain-interface/src/integritee/event_handler.rs @@ -0,0 +1,80 @@ +/* + 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. + +*/ + +use codec::Encode; + +pub use ita_sgx_runtime::{Balance, Index}; +use ita_stf::{Getter, TrustedCall, TrustedCallSigned}; +use itc_parentchain_indirect_calls_executor::error::Error; +use itp_stf_primitives::{traits::IndirectExecutor, types::TrustedOperation}; +use itp_types::parentchain::{AccountId, FilterEvents, HandleParentchainEvents, ParentchainError}; +use log::*; + +type Seed = [u8; 32]; + +const ALICE_ENCODED: Seed = [ + 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, + 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125, +]; + +const SHIELDING_ACCOUNT: AccountId = AccountId::new(ALICE_ENCODED); + +pub struct ParentchainEventHandler {} + +impl ParentchainEventHandler { + fn shield_funds>( + executor: &Executor, + account: &AccountId, + amount: Balance, + ) -> Result<(), Error> { + log::info!("shielding for {:?} amount {}", account, amount,); + let shard = executor.get_default_shard(); + let trusted_call = + TrustedCall::balance_shield(executor.get_enclave_account()?, account.clone(), amount); + let signed_trusted_call = executor.sign_call_with_self(&trusted_call, &shard)?; + let trusted_operation = + TrustedOperation::::indirect_call(signed_trusted_call); + + let encrypted_trusted_call = executor.encrypt(&trusted_operation.encode())?; + executor.submit_trusted_call(shard, encrypted_trusted_call); + + Ok(()) + } +} + +impl HandleParentchainEvents + for ParentchainEventHandler +where + Executor: IndirectExecutor, +{ + fn handle_events(executor: &Executor, events: impl FilterEvents) -> Result<(), Error> { + let filter_events = events.get_transfer_events(); + + if let Ok(events) = filter_events { + events + .iter() + .filter(|&event| event.to == SHIELDING_ACCOUNT) + .try_for_each(|event| { + info!("transfer_event: {}", event); + //call = IndirectCall::ShieldFunds(ShieldFundsArgs{ }) + Self::shield_funds(executor, &event.from, event.amount) + }) + .map_err(|_| ParentchainError::ShieldFundsFailure)?; + } + Ok(()) + } +} diff --git a/core/parentchain/indirect-calls-executor/src/parentchain_parser.rs b/app-libs/parentchain-interface/src/integritee/extrinsic_parser.rs similarity index 94% rename from core/parentchain/indirect-calls-executor/src/parentchain_parser.rs rename to app-libs/parentchain-interface/src/integritee/extrinsic_parser.rs index 93ae9e934b..925aca30ee 100644 --- a/core/parentchain/indirect-calls-executor/src/parentchain_parser.rs +++ b/app-libs/parentchain-interface/src/integritee/extrinsic_parser.rs @@ -62,11 +62,11 @@ where // `()` is a trick to stop decoding after the call index. So the remaining bytes // of `call` after decoding only contain the parentchain's dispatchable's arguments. let xt = UncheckedExtrinsicV4::< - Address, - (CallIndex, ()), - PairSignature, - Self::SignedExtra, - >::decode(call_mut)?; + Address, + (CallIndex, ()), + PairSignature, + Self::SignedExtra, + >::decode(call_mut)?; Ok(SemiOpaqueExtrinsic { signature: xt.signature, diff --git a/app-libs/parentchain-interface/src/integritee/mod.rs b/app-libs/parentchain-interface/src/integritee/mod.rs new file mode 100644 index 0000000000..c7dfe7a4b2 --- /dev/null +++ b/app-libs/parentchain-interface/src/integritee/mod.rs @@ -0,0 +1,108 @@ +/* + Copyright 2021 Integritee 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. + +*/ + +mod event_filter; +mod event_handler; +mod extrinsic_parser; +use crate::{ + decode_and_log_error, + indirect_calls::{invoke::InvokeArgs, shield_funds::ShieldFundsArgs}, + integritee::extrinsic_parser::ParseExtrinsic, +}; +use codec::{Decode, Encode}; +use core::marker::PhantomData; +pub use event_filter::FilterableEvents; +pub use event_handler::ParentchainEventHandler; +pub use extrinsic_parser::ParentchainExtrinsicParser; +use ita_stf::TrustedCallSigned; +use itc_parentchain_indirect_calls_executor::{ + error::{Error, Result}, + filter_metadata::FilterIntoDataFrom, + IndirectDispatch, +}; +use itp_node_api::metadata::NodeMetadataTrait; +use itp_stf_primitives::traits::IndirectExecutor; +use log::trace; +/// The default indirect call (extrinsic-triggered) of the Integritee-Parachain. +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub enum IndirectCall { + ShieldFunds(ShieldFundsArgs), + Invoke(InvokeArgs), +} + +impl> + IndirectDispatch for IndirectCall +{ + fn dispatch(&self, executor: &Executor) -> Result<()> { + trace!("dispatching indirect call {:?}", self); + match self { + IndirectCall::ShieldFunds(shieldfunds_args) => shieldfunds_args.dispatch(executor), + IndirectCall::Invoke(invoke_args) => invoke_args.dispatch(executor), + } + } +} + +/// Default filter we use for the Integritee-Parachain. +pub struct ShieldFundsAndInvokeFilter { + _phantom: PhantomData, +} + +impl FilterIntoDataFrom + for ShieldFundsAndInvokeFilter +where + ExtrinsicParser: ParseExtrinsic, +{ + type Output = IndirectCall; + type ParseParentchainMetadata = ExtrinsicParser; + + fn filter_into_from_metadata( + encoded_data: &[u8], + metadata: &NodeMetadata, + ) -> Option { + let call_mut = &mut &encoded_data[..]; + + // Todo: the filter should not need to parse, only filter. This should directly be configured + // in the indirect executor. + let xt = match Self::ParseParentchainMetadata::parse(call_mut) { + Ok(xt) => xt, + Err(e) => { + log::error!( + "[ShieldFundsAndInvokeFilter] Could not parse parentchain extrinsic: {:?}", + e + ); + return None + }, + }; + let index = xt.call_index; + let call_args = &mut &xt.call_args[..]; + log::trace!( + "[ShieldFundsAndInvokeFilter] attempting to execute indirect call with index {:?}", + index + ); + if index == metadata.shield_funds_call_indexes().ok()? { + log::debug!("executing shield funds call"); + let args = decode_and_log_error::(call_args)?; + Some(IndirectCall::ShieldFunds(args)) + } else if index == metadata.invoke_call_indexes().ok()? { + log::debug!("executing invoke call"); + let args = decode_and_log_error::(call_args)?; + Some(IndirectCall::Invoke(args)) + } else { + None + } + } +} diff --git a/app-libs/parentchain-interface/src/lib.rs b/app-libs/parentchain-interface/src/lib.rs new file mode 100644 index 0000000000..b1aff31ade --- /dev/null +++ b/app-libs/parentchain-interface/src/lib.rs @@ -0,0 +1,38 @@ +/* + Copyright 2021 Integritee 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. + +*/ + +#![cfg_attr(all(not(target_env = "sgx"), not(feature = "std")), no_std)] +#![cfg_attr(target_env = "sgx", feature(rustc_private))] + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use codec::Decode; +pub mod indirect_calls; +pub mod integritee; +pub mod target_a; +pub mod target_b; + +pub fn decode_and_log_error(encoded: &mut &[u8]) -> Option { + match V::decode(encoded) { + Ok(v) => Some(v), + Err(e) => { + log::warn!("Could not decode. {:?}", e); + None + }, + } +} diff --git a/app-libs/parentchain-interface/src/target_a/event_filter.rs b/app-libs/parentchain-interface/src/target_a/event_filter.rs new file mode 100644 index 0000000000..b3efc37129 --- /dev/null +++ b/app-libs/parentchain-interface/src/target_a/event_filter.rs @@ -0,0 +1,89 @@ +/* + Copyright 2021 Integritee 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. + +*/ +//! Various way to filter Parentchain events + +use itc_parentchain_indirect_calls_executor::event_filter::ToEvents; +use itp_api_client_types::Events; + +use itp_types::{ + parentchain::{ + BalanceTransfer, ExtrinsicFailed, ExtrinsicStatus, ExtrinsicSuccess, FilterEvents, + }, + H256, +}; +use std::vec::Vec; + +#[derive(Clone)] +pub struct FilterableEvents(pub Events); + +impl ToEvents> for FilterableEvents { + fn to_events(&self) -> &Events { + &self.0 + } +} + +impl From> for FilterableEvents { + fn from(ev: Events) -> Self { + Self(ev) + } +} + +impl FilterEvents for FilterableEvents { + type Error = itc_parentchain_indirect_calls_executor::Error; + + fn get_extrinsic_statuses(&self) -> core::result::Result, Self::Error> { + Ok(self + .to_events() + .iter() + .filter_map(|ev| { + ev.and_then(|ev| { + if (ev.as_event::()?).is_some() { + return Ok(Some(ExtrinsicStatus::Success)) + } + + if (ev.as_event::()?).is_some() { + return Ok(Some(ExtrinsicStatus::Failed)) + } + + Ok(None) + }) + .ok() + .flatten() + }) + .collect()) + } + + fn get_transfer_events(&self) -> core::result::Result, Self::Error> { + Ok(self + .to_events() + .iter() + .flatten() // flatten filters out the nones + .filter_map(|ev| match ev.as_event::() { + Ok(maybe_event) => { + if maybe_event.is_none() { + log::warn!("Transfer event does not exist in parentchain metadata"); + }; + maybe_event + }, + Err(e) => { + log::error!("Could not decode event: {:?}", e); + None + }, + }) + .collect()) + } +} diff --git a/app-libs/parentchain-interface/src/target_a/event_handler.rs b/app-libs/parentchain-interface/src/target_a/event_handler.rs new file mode 100644 index 0000000000..40dd0b8867 --- /dev/null +++ b/app-libs/parentchain-interface/src/target_a/event_handler.rs @@ -0,0 +1,37 @@ +/* + 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. + +*/ + +pub use ita_sgx_runtime::{Balance, Index}; + +use ita_stf::TrustedCallSigned; +use itc_parentchain_indirect_calls_executor::error::Error; +use itp_stf_primitives::traits::IndirectExecutor; +use itp_types::parentchain::{FilterEvents, HandleParentchainEvents}; +use log::*; + +pub struct ParentchainEventHandler {} + +impl HandleParentchainEvents + for ParentchainEventHandler +where + Executor: IndirectExecutor, +{ + fn handle_events(_executor: &Executor, _events: impl FilterEvents) -> Result<(), Error> { + debug!("not handling any events for target A"); + Ok(()) + } +} diff --git a/app-libs/parentchain-interface/src/target_a/extrinsic_parser.rs b/app-libs/parentchain-interface/src/target_a/extrinsic_parser.rs new file mode 100644 index 0000000000..925aca30ee --- /dev/null +++ b/app-libs/parentchain-interface/src/target_a/extrinsic_parser.rs @@ -0,0 +1,77 @@ +/* + 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. + +*/ + +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use itp_node_api::api_client::{ + Address, CallIndex, PairSignature, ParentchainSignedExtra, Signature, UncheckedExtrinsicV4, +}; + +pub struct ExtrinsicParser { + _phantom: PhantomData, +} + +/// Parses the extrinsics corresponding to the parentchain. +pub type ParentchainExtrinsicParser = ExtrinsicParser; + +/// Partially interpreted extrinsic containing the `signature` and the `call_index` whereas +/// the `call_args` remain in encoded form. +/// +/// Intended for usage, where the actual `call_args` form is unknown. +pub struct SemiOpaqueExtrinsic<'a, SignedExtra> { + /// Signature of the Extrinsic. + pub signature: Signature, + /// Call index of the dispatchable. + pub call_index: CallIndex, + /// Encoded arguments of the dispatchable corresponding to the `call_index`. + pub call_args: &'a [u8], +} + +/// Trait to extract signature and call indexes of an encoded [UncheckedExtrinsicV4]. +pub trait ParseExtrinsic { + /// Signed extra of the extrinsic. + type SignedExtra; + + fn parse(encoded_call: &[u8]) -> Result, codec::Error>; +} + +impl ParseExtrinsic for ExtrinsicParser +where + SignedExtra: Decode + Encode, +{ + type SignedExtra = SignedExtra; + + /// Extract a call index of an encoded call. + fn parse(encoded_call: &[u8]) -> Result, codec::Error> { + let call_mut = &mut &encoded_call[..]; + + // `()` is a trick to stop decoding after the call index. So the remaining bytes + // of `call` after decoding only contain the parentchain's dispatchable's arguments. + let xt = UncheckedExtrinsicV4::< + Address, + (CallIndex, ()), + PairSignature, + Self::SignedExtra, + >::decode(call_mut)?; + + Ok(SemiOpaqueExtrinsic { + signature: xt.signature, + call_index: xt.function.0, + call_args: call_mut, + }) + } +} diff --git a/app-libs/parentchain-interface/src/target_a/mod.rs b/app-libs/parentchain-interface/src/target_a/mod.rs new file mode 100644 index 0000000000..e26d2e45c0 --- /dev/null +++ b/app-libs/parentchain-interface/src/target_a/mod.rs @@ -0,0 +1,112 @@ +/* + Copyright 2021 Integritee 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. + +*/ +mod event_filter; +mod event_handler; +mod extrinsic_parser; +use crate::{ + decode_and_log_error, + indirect_calls::{ + transfer_to_alice_shields_funds::TransferToAliceShieldsFundsArgs, ALICE_ACCOUNT_ID, + }, +}; +use codec::{Decode, Encode}; +use core::marker::PhantomData; +pub use event_filter::FilterableEvents; +pub use event_handler::ParentchainEventHandler; +pub use extrinsic_parser::ParentchainExtrinsicParser; +use extrinsic_parser::ParseExtrinsic; +use ita_stf::TrustedCallSigned; +use itc_parentchain_indirect_calls_executor::{ + error::{Error, Result}, + filter_metadata::FilterIntoDataFrom, + IndirectDispatch, +}; +use itp_node_api::metadata::pallet_balances::BalancesCallIndexes; +use itp_stf_primitives::traits::IndirectExecutor; +use log::trace; + +/// The default indirect call (extrinsic-triggered) of the Target-A-Parachain. +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub enum IndirectCall { + TransferToAliceShieldsFunds(TransferToAliceShieldsFundsArgs), +} + +impl> + IndirectDispatch for IndirectCall +{ + fn dispatch(&self, executor: &Executor) -> Result<()> { + trace!("dispatching indirect call {:?}", self); + match self { + IndirectCall::TransferToAliceShieldsFunds(args) => args.dispatch(executor), + } + } +} + +/// Simple demo filter for testing. +/// +/// A transfer to Alice will issue the corresponding balance to Alice in the enclave. +/// It does not do anything else. +pub struct TransferToAliceShieldsFundsFilter { + _phantom: PhantomData, +} + +impl FilterIntoDataFrom + for TransferToAliceShieldsFundsFilter +where + ExtrinsicParser: ParseExtrinsic, +{ + type Output = IndirectCall; + type ParseParentchainMetadata = ExtrinsicParser; + + fn filter_into_from_metadata( + encoded_data: &[u8], + metadata: &NodeMetadata, + ) -> Option { + let call_mut = &mut &encoded_data[..]; + + // Todo: the filter should not need to parse, only filter. This should directly be configured + // in the indirect executor. + let xt = match Self::ParseParentchainMetadata::parse(call_mut) { + Ok(xt) => xt, + Err(e) => { + log::error!("[TransferToAliceShieldsFundsFilter] Could not parse parentchain extrinsic: {:?}", e); + return None + }, + }; + let index = xt.call_index; + let call_args = &mut &xt.call_args[..]; + log::trace!("[TransferToAliceShieldsFundsFilter] attempting to execute indirect call with index {:?}", index); + if index == metadata.transfer_call_indexes().ok()? + || index == metadata.transfer_keep_alive_call_indexes().ok()? + || index == metadata.transfer_allow_death_call_indexes().ok()? + { + log::debug!( + "found `transfer` or `transfer_allow_death` or `transfer_keep_alive` call." + ); + let args = decode_and_log_error::(call_args)?; + if args.destination == ALICE_ACCOUNT_ID.into() { + Some(IndirectCall::TransferToAliceShieldsFunds(args)) + } else { + log::debug!("Parentchain transfer was not for Alice; ignoring..."); + // No need to put it into the top pool if it isn't executed in the first place. + None + } + } else { + None + } + } +} diff --git a/app-libs/parentchain-interface/src/target_b/event_filter.rs b/app-libs/parentchain-interface/src/target_b/event_filter.rs new file mode 100644 index 0000000000..b3efc37129 --- /dev/null +++ b/app-libs/parentchain-interface/src/target_b/event_filter.rs @@ -0,0 +1,89 @@ +/* + Copyright 2021 Integritee 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. + +*/ +//! Various way to filter Parentchain events + +use itc_parentchain_indirect_calls_executor::event_filter::ToEvents; +use itp_api_client_types::Events; + +use itp_types::{ + parentchain::{ + BalanceTransfer, ExtrinsicFailed, ExtrinsicStatus, ExtrinsicSuccess, FilterEvents, + }, + H256, +}; +use std::vec::Vec; + +#[derive(Clone)] +pub struct FilterableEvents(pub Events); + +impl ToEvents> for FilterableEvents { + fn to_events(&self) -> &Events { + &self.0 + } +} + +impl From> for FilterableEvents { + fn from(ev: Events) -> Self { + Self(ev) + } +} + +impl FilterEvents for FilterableEvents { + type Error = itc_parentchain_indirect_calls_executor::Error; + + fn get_extrinsic_statuses(&self) -> core::result::Result, Self::Error> { + Ok(self + .to_events() + .iter() + .filter_map(|ev| { + ev.and_then(|ev| { + if (ev.as_event::()?).is_some() { + return Ok(Some(ExtrinsicStatus::Success)) + } + + if (ev.as_event::()?).is_some() { + return Ok(Some(ExtrinsicStatus::Failed)) + } + + Ok(None) + }) + .ok() + .flatten() + }) + .collect()) + } + + fn get_transfer_events(&self) -> core::result::Result, Self::Error> { + Ok(self + .to_events() + .iter() + .flatten() // flatten filters out the nones + .filter_map(|ev| match ev.as_event::() { + Ok(maybe_event) => { + if maybe_event.is_none() { + log::warn!("Transfer event does not exist in parentchain metadata"); + }; + maybe_event + }, + Err(e) => { + log::error!("Could not decode event: {:?}", e); + None + }, + }) + .collect()) + } +} diff --git a/app-libs/parentchain-interface/src/target_b/event_handler.rs b/app-libs/parentchain-interface/src/target_b/event_handler.rs new file mode 100644 index 0000000000..cb33206a8b --- /dev/null +++ b/app-libs/parentchain-interface/src/target_b/event_handler.rs @@ -0,0 +1,37 @@ +/* + 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. + +*/ + +pub use ita_sgx_runtime::{Balance, Index}; + +use ita_stf::TrustedCallSigned; +use itc_parentchain_indirect_calls_executor::error::Error; +use itp_stf_primitives::traits::IndirectExecutor; +use itp_types::parentchain::{FilterEvents, HandleParentchainEvents}; +use log::*; + +pub struct ParentchainEventHandler {} + +impl HandleParentchainEvents + for ParentchainEventHandler +where + Executor: IndirectExecutor, +{ + fn handle_events(_executor: &Executor, _events: impl FilterEvents) -> Result<(), Error> { + debug!("not handling any events for target B"); + Ok(()) + } +} diff --git a/app-libs/parentchain-interface/src/target_b/extrinsic_parser.rs b/app-libs/parentchain-interface/src/target_b/extrinsic_parser.rs new file mode 100644 index 0000000000..925aca30ee --- /dev/null +++ b/app-libs/parentchain-interface/src/target_b/extrinsic_parser.rs @@ -0,0 +1,77 @@ +/* + 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. + +*/ + +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use itp_node_api::api_client::{ + Address, CallIndex, PairSignature, ParentchainSignedExtra, Signature, UncheckedExtrinsicV4, +}; + +pub struct ExtrinsicParser { + _phantom: PhantomData, +} + +/// Parses the extrinsics corresponding to the parentchain. +pub type ParentchainExtrinsicParser = ExtrinsicParser; + +/// Partially interpreted extrinsic containing the `signature` and the `call_index` whereas +/// the `call_args` remain in encoded form. +/// +/// Intended for usage, where the actual `call_args` form is unknown. +pub struct SemiOpaqueExtrinsic<'a, SignedExtra> { + /// Signature of the Extrinsic. + pub signature: Signature, + /// Call index of the dispatchable. + pub call_index: CallIndex, + /// Encoded arguments of the dispatchable corresponding to the `call_index`. + pub call_args: &'a [u8], +} + +/// Trait to extract signature and call indexes of an encoded [UncheckedExtrinsicV4]. +pub trait ParseExtrinsic { + /// Signed extra of the extrinsic. + type SignedExtra; + + fn parse(encoded_call: &[u8]) -> Result, codec::Error>; +} + +impl ParseExtrinsic for ExtrinsicParser +where + SignedExtra: Decode + Encode, +{ + type SignedExtra = SignedExtra; + + /// Extract a call index of an encoded call. + fn parse(encoded_call: &[u8]) -> Result, codec::Error> { + let call_mut = &mut &encoded_call[..]; + + // `()` is a trick to stop decoding after the call index. So the remaining bytes + // of `call` after decoding only contain the parentchain's dispatchable's arguments. + let xt = UncheckedExtrinsicV4::< + Address, + (CallIndex, ()), + PairSignature, + Self::SignedExtra, + >::decode(call_mut)?; + + Ok(SemiOpaqueExtrinsic { + signature: xt.signature, + call_index: xt.function.0, + call_args: call_mut, + }) + } +} diff --git a/app-libs/parentchain-interface/src/target_b/mod.rs b/app-libs/parentchain-interface/src/target_b/mod.rs new file mode 100644 index 0000000000..265de1d119 --- /dev/null +++ b/app-libs/parentchain-interface/src/target_b/mod.rs @@ -0,0 +1,85 @@ +/* + Copyright 2021 Integritee 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. + +*/ + +/* + Copyright 2021 Integritee 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. + +*/ +mod event_filter; +mod event_handler; +mod extrinsic_parser; + +use codec::{Decode, Encode}; +use core::marker::PhantomData; +pub use event_filter::FilterableEvents; +pub use event_handler::ParentchainEventHandler; +pub use extrinsic_parser::ParentchainExtrinsicParser; +use extrinsic_parser::ParseExtrinsic; +use ita_stf::TrustedCallSigned; +use itc_parentchain_indirect_calls_executor::{ + error::{Error, Result}, + filter_metadata::FilterIntoDataFrom, + IndirectDispatch, +}; +use itp_node_api::metadata::pallet_balances::BalancesCallIndexes; +use itp_stf_primitives::traits::IndirectExecutor; +use log::error; + +/// The default indirect call (extrinsic-triggered) of the Target-A-Parachain. +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub enum IndirectCall {} + +impl> + IndirectDispatch for IndirectCall +{ + fn dispatch(&self, _executor: &Executor) -> Result<()> { + Err(Error::Other("no indirect calls defined for target_b".into())) + } +} + +pub struct TargetBExtrinsicFilter { + _phantom: PhantomData, +} + +impl FilterIntoDataFrom + for TargetBExtrinsicFilter +where + ExtrinsicParser: ParseExtrinsic, +{ + type Output = IndirectCall; + type ParseParentchainMetadata = ExtrinsicParser; + + fn filter_into_from_metadata( + _encoded_data: &[u8], + _metadata: &NodeMetadata, + ) -> Option { + error!("no indirect calls filter has been implemented for target_b"); + None + } +} diff --git a/app-libs/stf/Cargo.toml b/app-libs/stf/Cargo.toml index 85eb9da1d4..d0e036e176 100644 --- a/app-libs/stf/Cargo.toml +++ b/app-libs/stf/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" # crates.io codec = { version = "3.0.0", default-features = false, features = ["derive"], package = "parity-scale-codec" } derive_more = { version = "0.99.5" } +hex = { version = "0.4", default-features = false } log = { version = "0.4", default-features = false } rlp = { version = "0.5", default-features = false } sha3 = { version = "0.10", default-features = false } @@ -17,6 +18,7 @@ sgx_tstd = { branch = "master", features = ["untrusted_fs", "net", "backtrace"], # local crates ita-sgx-runtime = { default-features = false, path = "../sgx-runtime" } +itc-parentchain-indirect-calls-executor = { path = "../../core/parentchain/indirect-calls-executor", default-features = false } itp-hashing = { default-features = false, path = "../../core-primitives/hashing" } itp-node-api = { default-features = false, path = "../../core-primitives/node-api" } itp-node-api-metadata = { default-features = false, path = "../../core-primitives/node-api/metadata" } @@ -65,6 +67,7 @@ std = [ "rlp/std", # local "ita-sgx-runtime/std", + "itc-parentchain-indirect-calls-executor/std", "itp-hashing/std", "itp-sgx-externalities/std", "itp-stf-interface/std", diff --git a/cli/demo_shielding_unshielding.sh b/cli/demo_shielding_unshielding.sh index cb32046af2..e65a749258 100755 --- a/cli/demo_shielding_unshielding.sh +++ b/cli/demo_shielding_unshielding.sh @@ -195,8 +195,8 @@ wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} balance ${AMOUNT_TRANSFER} echo "✔ ok" echo "" -echo "* Un-shield ${AMOUNT_UNSHIELD} tokens from Alice's incognito account" -${CLIENT} trusted --mrenclave ${MRENCLAVE} --xt-signer //Alice unshield-funds ${ICGACCOUNTALICE} //Alice ${AMOUNT_UNSHIELD} +echo "* Un-shield ${AMOUNT_UNSHIELD} tokens from Alice's incognito account to Ferie's L1 account" +${CLIENT} trusted --mrenclave ${MRENCLAVE} unshield-funds ${ICGACCOUNTALICE} //Ferdie ${AMOUNT_UNSHIELD} echo "" echo "* Wait and assert Alice's incognito account balance... " diff --git a/cli/test_auto_shielding_with_transfer_bob.sh b/cli/test_auto_shielding_with_transfer_bob.sh new file mode 100644 index 0000000000..0917d54fa4 --- /dev/null +++ b/cli/test_auto_shielding_with_transfer_bob.sh @@ -0,0 +1,140 @@ +#!/bin/bash +set -euo pipefail + +# Verifies that auto shielding transfers sent to vault account: //Alice are verified from sender //Bob +# + +while getopts ":m:p:A:u:V:w:x:y:z:C:" opt; do + case $opt in + p) + INTEGRITEE_RPC_PORT=$OPTARG + ;; + A) + WORKER_1_PORT=$OPTARG + ;; + u) + INTEGRITEE_RPC_URL=$OPTARG + ;; + V) + WORKER_1_URL=$OPTARG + ;; + w) + TARGET_A_PARENTCHAIN_RPC_URL=$OPTARG + ;; + x) + TARGET_A_PARENTCHAIN_RPC_PORT=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + *) + echo "invalid arg ${OPTARG}" + exit 1 + esac +done + +# Using default port if none given as arguments. +INTEGRITEE_RPC_PORT=${INTEGRITEE_RPC_PORT:-9944} +INTEGRITEE_RPC_URL=${INTEGRITEE_RPC_URL:-"ws://127.0.0.1"} +TARGET_A_PARENTCHAIN_RPC_PORT=${TARGET_A_PARENTCHAIN_RPC_PORT:-9966} +TARGET_A_PARENTCHAIN_RPC_URL=${TARGET_A_PARENTCHAIN_RPC_URL:-"ws://127.0.0.1"} + +WORKER_1_PORT=${WORKER_1_PORT:-2000} +WORKER_1_URL=${WORKER_1_URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/integritee-cli"} + +echo "Using client binary ${CLIENT_BIN}" +${CLIENT_BIN} --version +echo "Using Integritee RPC uri ${INTEGRITEE_RPC_URL}:${INTEGRITEE_RPC_PORT}" +echo "Using Target A RPC uri ${TARGET_A_PARENTCHAIN_RPC_URL}:${TARGET_A_PARENTCHAIN_RPC_PORT}" +echo "Using trusted-worker 1 uri ${WORKER_1_URL}:${WORKER_1_PORT}" +echo "" + +# the parentchain token is 12 decimal +UNIT=$(( 10 ** 12 )) + +# make these amounts greater than ED +AMOUNT_SHIELD=$(( 6 * UNIT )) + +CLIENT="${CLIENT_BIN} -p ${INTEGRITEE_RPC_PORT} -P ${WORKER_1_PORT} -u ${INTEGRITEE_RPC_URL} -U ${WORKER_1_URL}" +CLIENT2="${CLIENT_BIN} -p ${TARGET_A_PARENTCHAIN_RPC_PORT} -P ${WORKER_1_PORT} -u ${TARGET_A_PARENTCHAIN_RPC_URL} -U ${WORKER_1_URL}" + +# interval and max rounds to wait to check the given account balance in sidechain +WAIT_INTERVAL_SECONDS=10 +WAIT_ROUNDS=20 + +# Poll and assert the given account's state is equal to expected, +# with timeout WAIT_INTERVAL_SECONDS * WAIT_ROUNDS +# usage: +# wait_assert_state +# the `state-name` has to be the supported subcommand, e.g. `balance`, `nonce` +function wait_assert_state() +{ + for i in $(seq 1 $WAIT_ROUNDS); do + sleep $WAIT_INTERVAL_SECONDS + state=$(${CLIENT} trusted --mrenclave "$1" "$3" "$2") + if [ $state -eq "$4" ]; then + return + else + : + fi + done + echo + echo "Assert $2 $3 failed, expected = $4, actual = $state" + exit 1 +} + +# Do a live query and assert the given account's state is equal to expected +# usage: +# assert_state +function assert_state() +{ + state=$(${CLIENT} trusted --mrenclave "$1" "$3" "$2") + if [ -z "$state" ]; then + echo "Query $2 $3 failed" + exit 1 + fi + + if [ $state -eq "$4" ]; then + return + fi + echo + echo "Assert $2 $3 failed, expected = $4, actual = $state" + exit 1 +} + +echo "* Query on-chain enclave registry:" +${CLIENT} list-workers +echo "" + +# this will always take the first MRENCLAVE found in the registry !! +read MRENCLAVE <<< $($CLIENT list-workers | awk '/ MRENCLAVE: / { print $2; exit }') +echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" + +[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } + +VAULTACCOUNT=//Alice +## Sender account to shield for +BOBTRUSTEDACCOUNT=//Bob +echo " Bob's trusted account (same as public account) = ${BOBTRUSTEDACCOUNT}" +echo "" + +# Assert the initial trusted balance of Alice incognito +TRUSTED_BALANCE_BOB=1000000000000000 +wait_assert_state ${MRENCLAVE} ${BOBTRUSTEDACCOUNT} balance ${TRUSTED_BALANCE_BOB} + + +echo "* Send ${AMOUNT_SHIELD} from //Bob to //Alice on the Target A parentchain, which should trigger the shield process" +${CLIENT2} transfer //Bob ${VAULTACCOUNT} ${AMOUNT_SHIELD} +echo "" + +echo "* Wait and assert Bob's incognito account balance, should be $(( TRUSTED_BALANCE_BOB + AMOUNT_SHIELD ))" +wait_assert_state ${MRENCLAVE} ${BOBTRUSTEDACCOUNT} balance $(( TRUSTED_BALANCE_BOB + AMOUNT_SHIELD )) +echo "✔ ok" + +echo "" +echo "-----------------------" +echo "✔ The test passed!" +echo "-----------------------" +echo "" diff --git a/core-primitives/stf-primitives/Cargo.toml b/core-primitives/stf-primitives/Cargo.toml index 77eee376cf..7935dd8fe6 100644 --- a/core-primitives/stf-primitives/Cargo.toml +++ b/core-primitives/stf-primitives/Cargo.toml @@ -25,4 +25,5 @@ std = [ "sp-core/std", "sp-std/std", "sp-runtime/std", + "itp-sgx-runtime-primitives/std", ] diff --git a/core-primitives/stf-primitives/src/lib.rs b/core-primitives/stf-primitives/src/lib.rs index 25ef681d8c..8e5ce6b1c0 100644 --- a/core-primitives/stf-primitives/src/lib.rs +++ b/core-primitives/stf-primitives/src/lib.rs @@ -16,6 +16,7 @@ */ #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; + pub mod error; pub mod traits; pub mod types; diff --git a/core-primitives/stf-primitives/src/traits.rs b/core-primitives/stf-primitives/src/traits.rs index 4ed77f7ae1..34add7ce81 100644 --- a/core-primitives/stf-primitives/src/traits.rs +++ b/core-primitives/stf-primitives/src/traits.rs @@ -14,8 +14,10 @@ limitations under the License. */ - use crate::types::{AccountId, KeyPair, ShardIdentifier}; +use alloc::vec::Vec; +use codec::{Decode, Encode}; +use core::fmt::Debug; use itp_sgx_runtime_primitives::types::Index; use sp_runtime::transaction_validity::{TransactionValidityError, ValidTransaction}; /// checks authorization of stf getters @@ -47,3 +49,26 @@ pub trait TrustedCallVerification { pub trait PoolTransactionValidation { fn validate(&self) -> Result; } + +/// Trait to be implemented on the executor to serve helper methods of the executor +/// to the `IndirectDispatch` implementation. +pub trait IndirectExecutor +where + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, +{ + fn submit_trusted_call(&self, shard: ShardIdentifier, encrypted_trusted_call: Vec); + + fn decrypt(&self, encrypted: &[u8]) -> Result, Error>; + + fn encrypt(&self, value: &[u8]) -> Result, Error>; + + fn get_enclave_account(&self) -> Result; + + fn get_default_shard(&self) -> ShardIdentifier; + + fn sign_call_with_self>( + &self, + trusted_call: &TC, + shard: &ShardIdentifier, + ) -> Result; +} diff --git a/core-primitives/test/Cargo.toml b/core-primitives/test/Cargo.toml index b448550ace..c01abd24ad 100644 --- a/core-primitives/test/Cargo.toml +++ b/core-primitives/test/Cargo.toml @@ -16,6 +16,7 @@ sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-s # substrate deps sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-io = { default-features = false, features = ["disable_oom", "disable_panic_handler", "disable_allocator"], path = "../../core-primitives/substrate-sgx/sp-io" } sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } @@ -32,25 +33,24 @@ itp-stf-state-handler = { path = "../stf-state-handler", default-features = fals itp-storage = { path = "../storage", default-features = false } itp-time-utils = { path = "../time-utils", default-features = false } itp-types = { path = "../types", default-features = false, features = ["test"] } -sp-io = { default-features = false, features = ["disable_oom", "disable_panic_handler", "disable_allocator"], path = "../../core-primitives/substrate-sgx/sp-io" } [features] default = ["std"] std = [ "codec/std", + "itp-enclave-bridge-storage/std", + "itp-node-api-metadata-provider/std", + "itp-node-api/std", + "itp-ocall-api/std", "itp-sgx-crypto/std", "itp-sgx-externalities/std", "itp-stf-interface/std", "itp-stf-primitives/std", "itp-stf-state-handler/std", "itp-storage/std", - "itp-enclave-bridge-storage/std", "itp-time-utils/std", "itp-types/std", - "itp-node-api/std", - "itp-node-api-metadata-provider/std", - "itp-ocall-api/std", "log/std", "sp-core/std", "sp-io/std", diff --git a/core-primitives/types/Cargo.toml b/core-primitives/types/Cargo.toml index 5d1bc84658..8e029a2280 100644 --- a/core-primitives/types/Cargo.toml +++ b/core-primitives/types/Cargo.toml @@ -16,6 +16,11 @@ serde_json = { version = "1.0", default-features = false, features = ["alloc"] } # local dependencies itp-sgx-runtime-primitives = { path = "../../core-primitives/sgx-runtime-primitives", default-features = false } +itp-stf-primitives = { path = "../../core-primitives/stf-primitives", default-features = false } +itp-utils = { path = "../../core-primitives/utils", default-features = false } + +# scs +substrate-api-client = { default-features = false, features = ["sync-api"], git = "https://github.com/scs/substrate-api-client.git", branch = "polkadot-v0.9.42-tag-v0.14.0" } # substrate-deps frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } @@ -40,6 +45,9 @@ std = [ "serde_json/std", "primitive-types/std", "itp-sgx-runtime-primitives/std", + "itp-stf-primitives/std", + "itp-utils/std", + "substrate-api-client/std", "teerex-primitives/std", "enclave-bridge-primitives/std", # substrate diff --git a/core-primitives/types/src/lib.rs b/core-primitives/types/src/lib.rs index 361bb9793e..08ff827db2 100644 --- a/core-primitives/types/src/lib.rs +++ b/core-primitives/types/src/lib.rs @@ -20,8 +20,6 @@ use crate::storage::StorageEntry; use codec::{Decode, Encode}; -#[cfg(feature = "sgx")] -use sgx_tstd as std; use sp_std::vec::Vec; pub mod parentchain; diff --git a/core-primitives/types/src/parentchain.rs b/core-primitives/types/src/parentchain.rs index 6439929129..7bb9047cc4 100644 --- a/core-primitives/types/src/parentchain.rs +++ b/core-primitives/types/src/parentchain.rs @@ -15,11 +15,14 @@ */ -//! Parentchain specific params. Be sure to change them if your node uses different types. - +use alloc::{format, vec::Vec}; use codec::{Decode, Encode}; +use core::fmt::Debug; +use itp_stf_primitives::traits::{IndirectExecutor, TrustedCallVerification}; +use itp_utils::stringify::account_id_to_string; +use sp_core::bounded::alloc; use sp_runtime::{generic::Header as HeaderG, traits::BlakeTwo256, MultiAddress, MultiSignature}; -use sp_std::vec::Vec; +use substrate_api_client::ac_node_api::StaticEvent; pub type StorageProof = Vec>; @@ -64,3 +67,85 @@ pub enum ParentchainId { pub trait IdentifyParentchain { fn parentchain_id(&self) -> ParentchainId; } + +pub trait FilterEvents { + type Error: From + core::fmt::Debug; + fn get_extrinsic_statuses(&self) -> core::result::Result, Self::Error>; + + fn get_transfer_events(&self) -> core::result::Result, Self::Error>; +} + +#[derive(Encode, Decode, Debug)] +pub struct ExtrinsicSuccess; + +impl StaticEvent for ExtrinsicSuccess { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "ExtrinsicSuccess"; +} + +#[derive(Encode, Decode)] +pub struct ExtrinsicFailed; + +impl StaticEvent for ExtrinsicFailed { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "ExtrinsicFailed"; +} + +#[derive(Debug)] +pub enum ExtrinsicStatus { + Success, + Failed, +} + +#[derive(Encode, Decode, Debug)] +pub struct BalanceTransfer { + pub from: AccountId, + pub to: AccountId, + pub amount: Balance, +} + +impl core::fmt::Display for BalanceTransfer { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + let message = format!( + "BalanceTransfer :: from: {}, to: {}, amount: {}", + account_id_to_string::(&self.from), + account_id_to_string::(&self.to), + self.amount + ); + write!(f, "{}", message) + } +} + +impl StaticEvent for BalanceTransfer { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Transfer"; +} + +pub trait HandleParentchainEvents +where + Executor: IndirectExecutor, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, +{ + fn handle_events( + executor: &Executor, + events: impl FilterEvents, + ) -> core::result::Result<(), Error>; +} + +#[derive(Debug)] +pub enum ParentchainError { + ShieldFundsFailure, +} + +impl core::fmt::Display for ParentchainError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + let message = match &self { + ParentchainError::ShieldFundsFailure => "Parentchain Error: ShieldFundsFailure", + }; + write!(f, "{}", message) + } +} + +impl From for () { + fn from(_: ParentchainError) -> Self {} +} diff --git a/core/offchain-worker-executor/Cargo.toml b/core/offchain-worker-executor/Cargo.toml index 3c94fec598..51dce2211a 100644 --- a/core/offchain-worker-executor/Cargo.toml +++ b/core/offchain-worker-executor/Cargo.toml @@ -57,6 +57,7 @@ std = [ "itp-stf-primitives/std", "itp-stf-state-handler/std", "itp-top-pool-author/std", + "itp-types/std", "sp-core/std", "sp-runtime/std", "thiserror", diff --git a/core/offchain-worker-executor/src/executor.rs b/core/offchain-worker-executor/src/executor.rs index ef9a502a8a..7b2f5b4df3 100644 --- a/core/offchain-worker-executor/src/executor.rs +++ b/core/offchain-worker-executor/src/executor.rs @@ -226,7 +226,7 @@ mod tests { use itp_test::mock::{ handle_state_mock::HandleStateMock, - stf_mock::{mock_top_direct_trusted_call_signed, GetterMock, TrustedCallSignedMock}, + stf_mock::{GetterMock, TrustedCallSignedMock}, }; use itp_top_pool_author::mocks::AuthorApiMock; use itp_types::Block as ParentchainBlock; diff --git a/core/parentchain/indirect-calls-executor/Cargo.toml b/core/parentchain/indirect-calls-executor/Cargo.toml index bc37518757..37d49102c8 100644 --- a/core/parentchain/indirect-calls-executor/Cargo.toml +++ b/core/parentchain/indirect-calls-executor/Cargo.toml @@ -10,7 +10,6 @@ sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sd sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } # local dependencies -ita-stf = { path = "../../../app-libs/stf", default-features = false } itp-api-client-types = { path = "../../../core-primitives/node-api/api-client-types", default-features = false } itp-node-api = { path = "../../../core-primitives/node-api", default-features = false } itp-ocall-api = { path = "../../../core-primitives/ocall-api", default-features = false } @@ -18,6 +17,7 @@ itp-sgx-crypto = { path = "../../../core-primitives/sgx/crypto", default-feature itp-sgx-runtime-primitives = { path = "../../../core-primitives/sgx-runtime-primitives", default-features = false } itp-stf-executor = { path = "../../../core-primitives/stf-executor", default-features = false } itp-stf-primitives = { path = "../../../core-primitives/stf-primitives", default-features = false } +itp-test = { path = "../../../core-primitives/test", default-features = false } itp-top-pool-author = { path = "../../../core-primitives/top-pool-author", default-features = false } itp-types = { path = "../../../core-primitives/types", default-features = false } itp-utils = { path = "../../../core-primitives/utils", default-features = false } @@ -55,13 +55,13 @@ std = [ "bs58/std", "codec/std", "futures", - "ita-stf/std", "itp-node-api/std", "itp-ocall-api/std", "itp-sgx-crypto/std", "itp-stf-executor/std", "itp-top-pool-author/std", "itp-api-client-types/std", + "itp-test/std", "itp-types/std", "itp-sgx-runtime-primitives/std", "log/std", @@ -74,10 +74,10 @@ std = [ sgx = [ "sgx_tstd", "futures_sgx", - "ita-stf/sgx", "itp-node-api/sgx", "itp-sgx-crypto/sgx", "itp-stf-executor/sgx", "itp-top-pool-author/sgx", + "itp-test/sgx", "thiserror_sgx", ] diff --git a/core/parentchain/indirect-calls-executor/src/error.rs b/core/parentchain/indirect-calls-executor/src/error.rs index 6a5b04161f..624138f634 100644 --- a/core/parentchain/indirect-calls-executor/src/error.rs +++ b/core/parentchain/indirect-calls-executor/src/error.rs @@ -18,6 +18,7 @@ #[cfg(all(not(feature = "std"), feature = "sgx"))] use crate::sgx_reexport_prelude::*; +use itp_types::parentchain::ParentchainError; use sgx_types::sgx_status_t; use std::{boxed::Box, format}; @@ -40,6 +41,12 @@ pub enum Error { Other(#[from] Box), } +impl From for Error { + fn from(e: ParentchainError) -> Self { + Self::Other(format!("{:?}", e).into()) + } +} + impl From for Error { fn from(sgx_status: sgx_status_t) -> Self { Self::Sgx(sgx_status) diff --git a/core/parentchain/indirect-calls-executor/src/event_filter.rs b/core/parentchain/indirect-calls-executor/src/event_filter.rs index 3209b07ece..ffb9882f58 100644 --- a/core/parentchain/indirect-calls-executor/src/event_filter.rs +++ b/core/parentchain/indirect-calls-executor/src/event_filter.rs @@ -16,121 +16,18 @@ */ //! Various way to filter Parentchain events -use crate::error::Result; -use codec::{Decode, Encode}; -use itp_api_client_types::{Events, StaticEvent}; -use itp_sgx_runtime_primitives::types::{AccountId, Balance}; -use itp_types::H256; -use itp_utils::stringify::account_id_to_string; -use std::{fmt::Display, format, vec::Vec}; +use crate::error::Error; -#[derive(Encode, Decode, Debug)] -pub struct ExtrinsicSuccess; +use itp_stf_primitives::error::StfError; -impl StaticEvent for ExtrinsicSuccess { - const PALLET: &'static str = "System"; - const EVENT: &'static str = "ExtrinsicSuccess"; -} - -#[derive(Encode, Decode)] -pub struct ExtrinsicFailed; - -impl StaticEvent for ExtrinsicFailed { - const PALLET: &'static str = "System"; - const EVENT: &'static str = "ExtrinsicFailed"; -} - -#[derive(Debug)] -pub enum ExtrinsicStatus { - Success, - Failed, -} - -#[derive(Encode, Decode, Debug)] -pub struct BalanceTransfer { - pub from: AccountId, - pub to: AccountId, - pub amount: Balance, -} - -impl StaticEvent for BalanceTransfer { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Transfer"; -} - -impl Display for BalanceTransfer { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let message = format!( - "BalanceTransfer :: from: {}, to: {}, amount: {}", - account_id_to_string::(&self.from), - account_id_to_string::(&self.to), - self.amount - ); - write!(f, "{}", message) - } -} - -pub trait FilterEvents { - fn get_extrinsic_statuses(&self) -> Result>; - - fn get_transfer_events(&self) -> Result>; -} - -impl FilterEvents for Events { - fn get_extrinsic_statuses(&self) -> Result> { - Ok(self - .iter() - .filter_map(|ev| { - ev.and_then(|ev| { - if (ev.as_event::()?).is_some() { - return Ok(Some(ExtrinsicStatus::Success)) - } - - if (ev.as_event::()?).is_some() { - return Ok(Some(ExtrinsicStatus::Failed)) - } +use std::format; - Ok(None) - }) - .ok() - .flatten() - }) - .collect()) - } - - fn get_transfer_events(&self) -> Result> { - Ok(self - .iter() - .flatten() // flatten filters out the nones - .filter_map(|ev| match ev.as_event::() { - Ok(maybe_event) => { - if maybe_event.is_none() { - log::warn!("Transfer event does not exist in parentchain metadata"); - }; - maybe_event - }, - Err(e) => { - log::error!("Could not decode event: {:?}", e); - None - }, - }) - .collect()) +impl From for Error { + fn from(a: StfError) -> Self { + Error::Other(format!("Error when shielding for privacy sidechain {:?}", a).into()) } } -pub struct MockEvents; - -impl FilterEvents for MockEvents { - fn get_extrinsic_statuses(&self) -> Result> { - Ok(Vec::from([ExtrinsicStatus::Success])) - } - - fn get_transfer_events(&self) -> Result> { - let transfer = BalanceTransfer { - to: [0u8; 32].into(), - from: [0u8; 32].into(), - amount: Balance::default(), - }; - Ok(Vec::from([transfer])) - } +pub trait ToEvents { + fn to_events(&self) -> &E; } diff --git a/core/parentchain/indirect-calls-executor/src/executor.rs b/core/parentchain/indirect-calls-executor/src/executor.rs index aff9dbc454..34946347ba 100644 --- a/core/parentchain/indirect-calls-executor/src/executor.rs +++ b/core/parentchain/indirect-calls-executor/src/executor.rs @@ -21,23 +21,28 @@ use crate::sgx_reexport_prelude::*; use crate::{ error::{Error, Result}, - event_filter::{ExtrinsicStatus, FilterEvents}, filter_metadata::{EventsFromMetadata, FilterIntoDataFrom}, - traits::{ExecuteIndirectCalls, IndirectDispatch, IndirectExecutor}, + traits::{ExecuteIndirectCalls, IndirectDispatch}, }; +use alloc::format; use binary_merkle_tree::merkle_root; -use codec::Encode; +use codec::{Decode, Encode}; use core::marker::PhantomData; -use ita_stf::{Getter, TrustedCall, TrustedCallSigned}; use itp_node_api::metadata::{ pallet_enclave_bridge::EnclaveBridgeCallIndexes, provider::AccessNodeMetadata, NodeMetadataTrait, }; use itp_sgx_crypto::{key_repository::AccessKey, ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}; use itp_stf_executor::traits::StfEnclaveSigning; -use itp_stf_primitives::types::AccountId; +use itp_stf_primitives::{ + traits::{IndirectExecutor, TrustedCallSigning, TrustedCallVerification}, + types::AccountId, +}; use itp_top_pool_author::traits::AuthorApi; -use itp_types::{OpaqueCall, ShardIdentifier, H256}; +use itp_types::{ + parentchain::{ExtrinsicStatus, FilterEvents, HandleParentchainEvents}, + OpaqueCall, ShardIdentifier, H256, +}; use log::*; use sp_core::blake2_256; use sp_runtime::traits::{Block as ParentchainBlockTrait, Header, Keccak256}; @@ -50,12 +55,15 @@ pub struct IndirectCallsExecutor< NodeMetadataProvider, IndirectCallsFilter, EventCreator, + ParentchainEventHandler, + TCS, + G, > { pub(crate) shielding_key_repo: Arc, pub(crate) stf_enclave_signer: Arc, pub(crate) top_pool_author: Arc, pub(crate) node_meta_data_provider: Arc, - _phantom: PhantomData<(IndirectCallsFilter, EventCreator)>, + _phantom: PhantomData<(IndirectCallsFilter, EventCreator, ParentchainEventHandler, TCS, G)>, } impl< ShieldingKeyRepository, @@ -64,6 +72,9 @@ impl< NodeMetadataProvider, IndirectCallsFilter, EventCreator, + ParentchainEventHandler, + TCS, + G, > IndirectCallsExecutor< ShieldingKeyRepository, @@ -72,6 +83,9 @@ impl< NodeMetadataProvider, IndirectCallsFilter, EventCreator, + ParentchainEventHandler, + TCS, + G, > { pub fn new( @@ -97,6 +111,9 @@ impl< NodeMetadataProvider, FilterIndirectCalls, EventCreator, + ParentchainEventHandler, + TCS, + G, > ExecuteIndirectCalls for IndirectCallsExecutor< ShieldingKeyRepository, @@ -105,17 +122,23 @@ impl< NodeMetadataProvider, FilterIndirectCalls, EventCreator, + ParentchainEventHandler, + TCS, + G, > where ShieldingKeyRepository: AccessKey, ::KeyType: ShieldingCryptoDecrypt + ShieldingCryptoEncrypt, - StfEnclaveSigner: StfEnclaveSigning, - TopPoolAuthor: AuthorApi + Send + Sync + 'static, + StfEnclaveSigner: StfEnclaveSigning, + TopPoolAuthor: AuthorApi + Send + Sync + 'static, NodeMetadataProvider: AccessNodeMetadata, FilterIndirectCalls: FilterIntoDataFrom, NodeMetadataProvider::MetadataType: NodeMetadataTrait + Clone, - FilterIndirectCalls::Output: IndirectDispatch + Encode + Debug, + FilterIndirectCalls::Output: IndirectDispatch + Encode + Debug, EventCreator: EventsFromMetadata, + ParentchainEventHandler: HandleParentchainEvents, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, { fn execute_indirect_calls_in_extrinsics( &self, @@ -138,14 +161,12 @@ impl< })? .ok_or_else(|| Error::Other("Could not create events from metadata".into()))?; - let xt_statuses = events.get_extrinsic_statuses()?; + let xt_statuses = events.get_extrinsic_statuses().map_err(|e| { + Error::Other(format!("Error when shielding for privacy sidechain {:?}", e).into()) + })?; trace!("xt_statuses:: {:?}", xt_statuses); - let filter_events = events.get_transfer_events(); - - if let Ok(events) = filter_events { - events.iter().for_each(|event| info!("Found transfer_event: {:?}", event)) - } + ParentchainEventHandler::handle_events(self, events)?; // This would be catastrophic but should never happen if xt_statuses.len() != block.extrinsics().len() { @@ -214,7 +235,10 @@ impl< NodeMetadataProvider, FilterIndirectCalls, EventFilter, - > IndirectExecutor + PrivacySidechain, + TCS, + G, + > IndirectExecutor for IndirectCallsExecutor< ShieldingKeyRepository, StfEnclaveSigner, @@ -222,12 +246,17 @@ impl< NodeMetadataProvider, FilterIndirectCalls, EventFilter, + PrivacySidechain, + TCS, + G, > where ShieldingKeyRepository: AccessKey, ::KeyType: ShieldingCryptoDecrypt + ShieldingCryptoEncrypt, - StfEnclaveSigner: StfEnclaveSigning, - TopPoolAuthor: AuthorApi + Send + Sync + 'static, + StfEnclaveSigner: StfEnclaveSigning, + TopPoolAuthor: AuthorApi + Send + Sync + 'static, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, { fn submit_trusted_call(&self, shard: ShardIdentifier, encrypted_trusted_call: Vec) { if let Err(e) = futures::executor::block_on( @@ -255,11 +284,11 @@ impl< self.top_pool_author.list_handled_shards().first().copied().unwrap_or_default() } - fn sign_call_with_self( + fn sign_call_with_self>( &self, - trusted_call: &TrustedCall, + trusted_call: &TC, shard: &ShardIdentifier, - ) -> Result { + ) -> Result { Ok(self.stf_enclave_signer.sign_call_with_self(trusted_call, shard)?) } } @@ -271,10 +300,7 @@ pub(crate) fn hash_of(xt: &T) -> H256 { #[cfg(test)] mod test { use super::*; - use crate::{ - filter_metadata::{ShieldFundsAndInvokeFilter, TestEventCreator}, - parentchain_parser::ParentchainExtrinsicParser, - }; + use crate::mock::*; use codec::{Decode, Encode}; use itc_parentchain_test::ParentchainBlockBuilder; use itp_node_api::{ @@ -290,7 +316,10 @@ mod test { traits::TrustedCallVerification, types::{AccountId, TrustedOperation}, }; - use itp_test::mock::shielding_crypto_mock::ShieldingCryptoMock; + use itp_test::mock::{ + shielding_crypto_mock::ShieldingCryptoMock, + stf_mock::{GetterMock, TrustedCallSignedMock}, + }; use itp_top_pool_author::mocks::AuthorApiMock; use itp_types::{ parentchain::Address, Block, CallWorkerFn, Request, ShardIdentifier, ShieldFundsFn, @@ -301,18 +330,22 @@ mod test { type TestShieldingKeyRepo = KeyRepositoryMock; type TestStfEnclaveSigner = StfEnclaveSignerMock; - type TestTopPoolAuthor = AuthorApiMock; + type TestTopPoolAuthor = AuthorApiMock; type TestNodeMetadataRepository = NodeMetadataRepository; type TestIndirectCallExecutor = IndirectCallsExecutor< TestShieldingKeyRepo, TestStfEnclaveSigner, TestTopPoolAuthor, TestNodeMetadataRepository, - ShieldFundsAndInvokeFilter, + MockExtrinsicFilter, TestEventCreator, + MockParentchainEventHandler, + TrustedCallSignedMock, + GetterMock, >; type Seed = [u8; 32]; + const TEST_SEED: Seed = *b"12345678901234567890123456789012"; #[test] @@ -362,7 +395,7 @@ mod test { let submitted_extrinsic = top_pool_author.pending_tops(shard_id()).unwrap().first().cloned().unwrap(); let decrypted_extrinsic = shielding_key.decrypt(&submitted_extrinsic).unwrap(); - let decoded_operation = TrustedOperation::::decode( + let decoded_operation = TrustedOperation::::decode( &mut decrypted_extrinsic.as_slice(), ) .unwrap(); @@ -471,6 +504,7 @@ mod test { ParentchainAdditionalParams::default(), ) } + fn test_fixtures( mr_enclave: [u8; 32], metadata: NodeMetadataMock, diff --git a/core/parentchain/indirect-calls-executor/src/filter_metadata.rs b/core/parentchain/indirect-calls-executor/src/filter_metadata.rs index 410432516b..5dea343f67 100644 --- a/core/parentchain/indirect-calls-executor/src/filter_metadata.rs +++ b/core/parentchain/indirect-calls-executor/src/filter_metadata.rs @@ -15,23 +15,13 @@ */ -use crate::{ - error::Result, - event_filter::{FilterEvents, MockEvents}, - indirect_calls::{ - InvokeArgs, ShieldFundsArgs, TransferToAliceShieldsFundsArgs, ALICE_ACCOUNT_ID, - }, - parentchain_parser::ParseExtrinsic, - IndirectDispatch, IndirectExecutor, -}; +use crate::{error::Result, IndirectDispatch}; use codec::{Decode, Encode}; use core::marker::PhantomData; use itp_api_client_types::{Events, Metadata}; -use itp_node_api::metadata::{ - pallet_balances::BalancesCallIndexes, NodeMetadata, NodeMetadataTrait, -}; -use itp_types::H256; -use log::trace; +use itp_node_api::metadata::NodeMetadata; +use itp_stf_primitives::traits::IndirectExecutor; +use itp_types::{parentchain::FilterEvents, H256}; pub trait EventsFromMetadata { type Output: FilterEvents; @@ -43,10 +33,16 @@ pub trait EventsFromMetadata { ) -> Option; } -pub struct EventCreator; +pub struct EventCreator { + _phantom: PhantomData, +} -impl + Clone> EventsFromMetadata for EventCreator { - type Output = Events; +impl + Clone, FilterableEvents> EventsFromMetadata + for EventCreator +where + FilterableEvents: From> + FilterEvents, +{ + type Output = FilterableEvents; fn create_from_metadata( metadata: NodeMetadata, @@ -54,21 +50,7 @@ impl + Clone> EventsFromMetadata f events: &[u8], ) -> Option { let raw_metadata: Metadata = metadata.try_into().ok()?; - Some(Events::::new(raw_metadata, block_hash, events.to_vec())) - } -} - -pub struct TestEventCreator; - -impl EventsFromMetadata for TestEventCreator { - type Output = MockEvents; - - fn create_from_metadata( - _metadata: NodeMetadata, - _block_hash: H256, - _events: &[u8], - ) -> Option { - Some(MockEvents) + Some(Events::::new(raw_metadata, block_hash, events.to_vec()).into()) } } @@ -91,145 +73,11 @@ pub trait FilterIntoDataFrom { /// Indirect calls filter denying all indirect calls. pub struct DenyAll; -/// Simple demo filter for testing. -/// -/// A transfer to Alice will issue the corresponding balance to Alice in the enclave. -/// It does not do anything else. -pub struct TransferToAliceShieldsFundsFilter { - _phantom: PhantomData, -} -/// Default filter we use for the Integritee-Parachain. -pub struct ShieldFundsAndInvokeFilter { - _phantom: PhantomData, -} - -impl FilterIntoDataFrom - for ShieldFundsAndInvokeFilter -where - ExtrinsicParser: ParseExtrinsic, -{ - type Output = IndirectCall; - type ParseParentchainMetadata = ExtrinsicParser; - - fn filter_into_from_metadata( - encoded_data: &[u8], - metadata: &NodeMetadata, - ) -> Option { - let call_mut = &mut &encoded_data[..]; - - // Todo: the filter should not need to parse, only filter. This should directly be configured - // in the indirect executor. - let xt = match Self::ParseParentchainMetadata::parse(call_mut) { - Ok(xt) => xt, - Err(e) => { - log::error!( - "[ShieldFundsAndInvokeFilter] Could not parse parentchain extrinsic: {:?}", - e - ); - return None - }, - }; - let index = xt.call_index; - let call_args = &mut &xt.call_args[..]; - log::trace!( - "[ShieldFundsAndInvokeFilter] attempting to execute indirect call with index {:?}", - index - ); - if index == metadata.shield_funds_call_indexes().ok()? { - log::debug!("executing shield funds call"); - let args = decode_and_log_error::(call_args)?; - Some(IndirectCall::ShieldFunds(args)) - } else if index == metadata.invoke_call_indexes().ok()? { - log::debug!("executing invoke call"); - let args = decode_and_log_error::(call_args)?; - Some(IndirectCall::Invoke(args)) - } else { - None - } - } -} - -impl FilterIntoDataFrom - for TransferToAliceShieldsFundsFilter -where - ExtrinsicParser: ParseExtrinsic, -{ - type Output = IndirectCall; - type ParseParentchainMetadata = ExtrinsicParser; - - fn filter_into_from_metadata( - encoded_data: &[u8], - metadata: &NodeMetadata, - ) -> Option { - let call_mut = &mut &encoded_data[..]; - - // Todo: the filter should not need to parse, only filter. This should directly be configured - // in the indirect executor. - let xt = match Self::ParseParentchainMetadata::parse(call_mut) { - Ok(xt) => xt, - Err(e) => { - log::error!("[TransferToAliceShieldsFundsFilter] Could not parse parentchain extrinsic: {:?}", e); - return None - }, - }; - let index = xt.call_index; - let call_args = &mut &xt.call_args[..]; - log::trace!("[TransferToAliceShieldsFundsFilter] attempting to execute indirect call with index {:?}", index); - if index == metadata.transfer_call_indexes().ok()? - || index == metadata.transfer_keep_alive_call_indexes().ok()? - || index == metadata.transfer_allow_death_call_indexes().ok()? - { - log::debug!( - "found `transfer` or `transfer_allow_death` or `transfer_keep_alive` call." - ); - let args = decode_and_log_error::(call_args)?; - if args.destination == ALICE_ACCOUNT_ID.into() { - Some(IndirectCall::TransferToAliceShieldsFunds(args)) - } else { - log::debug!("Parentchain transfer was not for Alice; ignoring..."); - // No need to put it into the top pool if it isn't executed in the first place. - None - } - } else { - None - } - } -} - -/// The default indirect call of the Integritee-Parachain. -/// -/// Todo: Move or provide a template in app-libs such that users -/// can implemeent their own indirect call there. -#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] -pub enum IndirectCall { - ShieldFunds(ShieldFundsArgs), - Invoke(InvokeArgs), - TransferToAliceShieldsFunds(TransferToAliceShieldsFundsArgs), -} - -impl IndirectDispatch for IndirectCall { - fn dispatch(&self, executor: &Executor) -> Result<()> { - trace!("dispatching indirect call {:?}", self); - match self { - IndirectCall::ShieldFunds(shieldfunds_args) => shieldfunds_args.dispatch(executor), - IndirectCall::Invoke(invoke_args) => invoke_args.dispatch(executor), - IndirectCall::TransferToAliceShieldsFunds(args) => args.dispatch(executor), - } - } -} - -fn decode_and_log_error(encoded: &mut &[u8]) -> Option { - match V::decode(encoded) { - Ok(v) => Some(v), - Err(e) => { - log::warn!("Could not decode. {:?}", e); - None - }, - } -} - mod seal { use super::*; + use crate::Error; + use core::fmt::Debug; + use itp_stf_primitives::traits::TrustedCallVerification; /// Stub struct for the `DenyAll` filter that never executes anything. #[derive(Debug, Encode)] @@ -244,7 +92,10 @@ mod seal { } } - impl IndirectDispatch for CantExecute { + impl, TCS> IndirectDispatch for CantExecute + where + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, + { fn dispatch(&self, _: &Executor) -> Result<()> { // We should never get here because `CantExecute` is in a private module and the trait // implementation is sealed and always returns `None` instead of a `CantExecute` instance. diff --git a/core/parentchain/indirect-calls-executor/src/lib.rs b/core/parentchain/indirect-calls-executor/src/lib.rs index bd7935a898..57b0911e87 100644 --- a/core/parentchain/indirect-calls-executor/src/lib.rs +++ b/core/parentchain/indirect-calls-executor/src/lib.rs @@ -27,6 +27,8 @@ #[cfg(all(feature = "std", feature = "sgx"))] compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); +extern crate alloc; + #[cfg(all(not(feature = "std"), feature = "sgx"))] extern crate sgx_tstd as std; @@ -37,15 +39,14 @@ pub mod sgx_reexport_prelude { pub use thiserror_sgx as thiserror; } -mod event_filter; mod executor; -mod traits; +pub mod mock; +pub mod traits; pub mod error; +pub mod event_filter; pub mod filter_metadata; -pub mod indirect_calls; -pub mod parentchain_parser; pub use error::{Error, Result}; pub use executor::IndirectCallsExecutor; -pub use traits::{ExecuteIndirectCalls, IndirectDispatch, IndirectExecutor}; +pub use traits::{ExecuteIndirectCalls, IndirectDispatch}; diff --git a/core/parentchain/indirect-calls-executor/src/mock.rs b/core/parentchain/indirect-calls-executor/src/mock.rs new file mode 100644 index 0000000000..a12a4b820c --- /dev/null +++ b/core/parentchain/indirect-calls-executor/src/mock.rs @@ -0,0 +1,241 @@ +use crate::{ + error::{Error, Result as ICResult}, + filter_metadata::{EventsFromMetadata, FilterIntoDataFrom}, + IndirectDispatch, +}; +use codec::{Decode, Encode}; +use core::marker::PhantomData; + +use itp_node_api::{ + api_client::{CallIndex, PairSignature, UncheckedExtrinsicV4}, + metadata::NodeMetadataTrait, +}; +use itp_sgx_runtime_primitives::types::{AccountId, Balance}; +use itp_stf_primitives::{traits::IndirectExecutor, types::Signature}; +use itp_test::mock::stf_mock::{GetterMock, TrustedCallMock, TrustedCallSignedMock}; +use itp_types::{ + parentchain::{BalanceTransfer, ExtrinsicStatus, FilterEvents, HandleParentchainEvents}, + Address, Request, ShardIdentifier, H256, +}; +use log::*; +use std::vec::Vec; + +/// Default filter we use for the Integritee-Parachain. +pub struct MockExtrinsicFilter { + _phantom: PhantomData, +} + +impl FilterIntoDataFrom + for MockExtrinsicFilter +where + ExtrinsicParser: ParseExtrinsic, +{ + type Output = IndirectCall; + type ParseParentchainMetadata = ExtrinsicParser; + + fn filter_into_from_metadata( + encoded_data: &[u8], + metadata: &NodeMetadata, + ) -> Option { + let call_mut = &mut &encoded_data[..]; + + // Todo: the filter should not need to parse, only filter. This should directly be configured + // in the indirect executor. + let xt = match Self::ParseParentchainMetadata::parse(call_mut) { + Ok(xt) => xt, + Err(e) => { + log::error!( + "[ShieldFundsAndInvokeFilter] Could not parse parentchain extrinsic: {:?}", + e + ); + return None + }, + }; + let index = xt.call_index; + let call_args = &mut &xt.call_args[..]; + log::trace!( + "[ShieldFundsAndInvokeFilter] attempting to execute indirect call with index {:?}", + index + ); + if index == metadata.shield_funds_call_indexes().ok()? { + log::debug!("executing shield funds call"); + let args = ShieldFundsArgs::decode(call_args).unwrap(); + Some(IndirectCall::ShieldFunds(args)) + } else if index == metadata.invoke_call_indexes().ok()? { + log::debug!("executing invoke call"); + let args = InvokeArgs::decode(call_args).unwrap(); + Some(IndirectCall::Invoke(args)) + } else { + None + } + } +} +pub struct ExtrinsicParser { + _phantom: PhantomData, +} +use itp_api_client_types::ParentchainSignedExtra; +use itp_stf_primitives::types::TrustedOperation; + +/// Parses the extrinsics corresponding to the parentchain. +pub type MockParentchainExtrinsicParser = ExtrinsicParser; + +/// Partially interpreted extrinsic containing the `signature` and the `call_index` whereas +/// the `call_args` remain in encoded form. +/// +/// Intended for usage, where the actual `call_args` form is unknown. +pub struct SemiOpaqueExtrinsic<'a> { + /// Signature of the Extrinsic. + pub signature: Signature, + /// Call index of the dispatchable. + pub call_index: CallIndex, + /// Encoded arguments of the dispatchable corresponding to the `call_index`. + pub call_args: &'a [u8], +} + +/// Trait to extract signature and call indexes of an encoded [UncheckedExtrinsicV4]. +pub trait ParseExtrinsic { + /// Signed extra of the extrinsic. + type SignedExtra; + + fn parse(encoded_call: &[u8]) -> Result; +} + +impl ParseExtrinsic for ExtrinsicParser +where + SignedExtra: Decode + Encode, +{ + type SignedExtra = SignedExtra; + + /// Extract a call index of an encoded call. + fn parse(encoded_call: &[u8]) -> Result { + let call_mut = &mut &encoded_call[..]; + + // `()` is a trick to stop decoding after the call index. So the remaining bytes + // of `call` after decoding only contain the parentchain's dispatchable's arguments. + let xt = UncheckedExtrinsicV4::< + Address, + (CallIndex, ()), + PairSignature, + Self::SignedExtra, + >::decode(call_mut)?; + + Ok(SemiOpaqueExtrinsic { + signature: xt.signature.unwrap().1, + call_index: xt.function.0, + call_args: call_mut, + }) + } +} +/// The default indirect call (extrinsic-triggered) of the Integritee-Parachain. +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub enum IndirectCall { + ShieldFunds(ShieldFundsArgs), + Invoke(InvokeArgs), +} + +impl> + IndirectDispatch for IndirectCall +{ + fn dispatch(&self, executor: &Executor) -> ICResult<()> { + trace!("dispatching indirect call {:?}", self); + match self { + IndirectCall::ShieldFunds(shieldfunds_args) => shieldfunds_args.dispatch(executor), + IndirectCall::Invoke(invoke_args) => invoke_args.dispatch(executor), + } + } +} + +pub struct TestEventCreator; + +impl EventsFromMetadata for TestEventCreator { + type Output = MockEvents; + + fn create_from_metadata( + _metadata: NodeMetadata, + _block_hash: H256, + _events: &[u8], + ) -> Option { + Some(MockEvents) + } +} + +pub struct MockEvents; + +impl FilterEvents for MockEvents { + type Error = (); + fn get_extrinsic_statuses(&self) -> core::result::Result, Self::Error> { + Ok(Vec::from([ExtrinsicStatus::Success])) + } + + fn get_transfer_events(&self) -> core::result::Result, Self::Error> { + let transfer = BalanceTransfer { + to: [0u8; 32].into(), + from: [0u8; 32].into(), + amount: Balance::default(), + }; + Ok(Vec::from([transfer])) + } +} + +pub struct MockParentchainEventHandler {} + +impl HandleParentchainEvents + for MockParentchainEventHandler +where + Executor: IndirectExecutor, +{ + fn handle_events( + _: &Executor, + _: impl itp_types::parentchain::FilterEvents, + ) -> core::result::Result<(), Error> { + Ok(()) + } +} + +/// Arguments of the Integritee-Parachain's shield fund dispatchable. +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub struct ShieldFundsArgs { + shard: ShardIdentifier, + account_encrypted: Vec, + amount: Balance, +} + +impl> + IndirectDispatch for ShieldFundsArgs +{ + fn dispatch(&self, executor: &Executor) -> ICResult<()> { + info!("Found ShieldFunds extrinsic in block: \nAccount Encrypted {:?} \nAmount: {} \nShard: {}", + self.account_encrypted, self.amount, bs58::encode(self.shard.encode()).into_string()); + + debug!("decrypt the account id"); + let account_vec = executor.decrypt(&self.account_encrypted)?; + let _account = AccountId::decode(&mut account_vec.as_slice())?; + + let enclave_account_id = executor.get_enclave_account()?; + let trusted_call = TrustedCallMock::noop(enclave_account_id); + let signed_trusted_call = executor.sign_call_with_self(&trusted_call, &self.shard)?; + let trusted_operation = + TrustedOperation::::indirect_call( + signed_trusted_call, + ); + + let encrypted_trusted_call = executor.encrypt(&trusted_operation.encode())?; + executor.submit_trusted_call(self.shard, encrypted_trusted_call); + Ok(()) + } +} + +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub struct InvokeArgs { + request: Request, +} + +impl> + IndirectDispatch for InvokeArgs +{ + fn dispatch(&self, executor: &Executor) -> ICResult<()> { + log::debug!("Found trusted call extrinsic, submitting it to the top pool"); + executor.submit_trusted_call(self.request.shard, self.request.cyphertext.clone()); + Ok(()) + } +} diff --git a/core/parentchain/indirect-calls-executor/src/traits.rs b/core/parentchain/indirect-calls-executor/src/traits.rs index 9e069bc2ec..f67b2e7634 100644 --- a/core/parentchain/indirect-calls-executor/src/traits.rs +++ b/core/parentchain/indirect-calls-executor/src/traits.rs @@ -15,10 +15,11 @@ */ -use crate::error::Result; -use ita_stf::{TrustedCall, TrustedCallSigned}; -use itp_stf_primitives::types::AccountId; -use itp_types::{OpaqueCall, ShardIdentifier, H256}; +use crate::{error::Result, Error}; +use codec::{Decode, Encode}; +use core::fmt::Debug; +use itp_stf_primitives::traits::{IndirectExecutor, TrustedCallVerification}; +use itp_types::{OpaqueCall, H256}; use sp_runtime::traits::{Block as ParentchainBlockTrait, Header}; use std::vec::Vec; @@ -49,26 +50,9 @@ pub trait ExecuteIndirectCalls { } /// Trait that should be implemented on indirect calls to be executed. -pub trait IndirectDispatch { +pub trait IndirectDispatch, TCS> +where + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, +{ fn dispatch(&self, executor: &E) -> Result<()>; } - -/// Trait to be implemented on the executor to serve helper methods of the executor -/// to the `IndirectDispatch` implementation. -pub trait IndirectExecutor { - fn submit_trusted_call(&self, shard: ShardIdentifier, encrypted_trusted_call: Vec); - - fn decrypt(&self, encrypted: &[u8]) -> Result>; - - fn encrypt(&self, value: &[u8]) -> Result>; - - fn get_enclave_account(&self) -> Result; - - fn get_default_shard(&self) -> ShardIdentifier; - - fn sign_call_with_self( - &self, - trusted_call: &TrustedCall, - shard: &ShardIdentifier, - ) -> Result; -} diff --git a/enclave-runtime/Cargo.lock b/enclave-runtime/Cargo.lock index 10dd0550fe..794b574e44 100644 --- a/enclave-runtime/Cargo.lock +++ b/enclave-runtime/Cargo.lock @@ -782,6 +782,7 @@ dependencies = [ "hex", "ipfs-unixfs", "ita-oracle", + "ita-parentchain-interface", "ita-sgx-runtime", "ita-stf", "itc-direct-rpc-server", @@ -1659,6 +1660,36 @@ dependencies = [ "url", ] +[[package]] +name = "ita-parentchain-interface" +version = "0.9.0" +dependencies = [ + "binary-merkle-tree", + "bs58", + "frame-support", + "futures 0.3.8", + "ita-sgx-runtime", + "ita-stf", + "itc-parentchain-indirect-calls-executor", + "itp-api-client-types", + "itp-node-api", + "itp-ocall-api", + "itp-sgx-crypto", + "itp-sgx-runtime-primitives", + "itp-stf-executor", + "itp-stf-primitives", + "itp-top-pool-author", + "itp-types", + "itp-utils", + "log", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-core", + "sp-runtime", + "thiserror 1.0.9", +] + [[package]] name = "ita-sgx-runtime" version = "0.9.0" @@ -1700,7 +1731,9 @@ dependencies = [ "derive_more", "frame-support", "frame-system", + "hex", "ita-sgx-runtime", + "itc-parentchain-indirect-calls-executor", "itp-hashing", "itp-node-api", "itp-node-api-metadata", @@ -1817,7 +1850,6 @@ dependencies = [ "binary-merkle-tree", "bs58", "futures 0.3.8", - "ita-stf", "itp-api-client-types", "itp-node-api", "itp-ocall-api", @@ -1825,6 +1857,7 @@ dependencies = [ "itp-sgx-runtime-primitives", "itp-stf-executor", "itp-stf-primitives", + "itp-test", "itp-top-pool-author", "itp-types", "itp-utils", @@ -2391,6 +2424,8 @@ dependencies = [ "enclave-bridge-primitives", "frame-system", "itp-sgx-runtime-primitives", + "itp-stf-primitives", + "itp-utils", "pallet-balances", "parity-scale-codec", "primitive-types", @@ -2399,6 +2434,7 @@ dependencies = [ "sp-core", "sp-runtime", "sp-std", + "substrate-api-client", "teerex-primitives", ] diff --git a/enclave-runtime/Cargo.toml b/enclave-runtime/Cargo.toml index 83fcbe4e6f..50a6b499b5 100644 --- a/enclave-runtime/Cargo.toml +++ b/enclave-runtime/Cargo.toml @@ -93,6 +93,7 @@ teerex-primitives = { default-features = false, git = "https://github.com/integr # local deps ita-oracle = { path = "../app-libs/oracle", default-features = false, optional = true, features = ["sgx"] } +ita-parentchain-interface = { path = "../app-libs/parentchain-interface", default-features = false, features = ["sgx"] } ita-sgx-runtime = { path = "../app-libs/sgx-runtime", default-features = false } ita-stf = { path = "../app-libs/stf", default-features = false, features = ["sgx"] } itc-direct-rpc-server = { path = "../core/direct-rpc-server", default-features = false, features = ["sgx"] } diff --git a/enclave-runtime/README.md b/enclave-runtime/README.md new file mode 100644 index 0000000000..a4b88a52d1 --- /dev/null +++ b/enclave-runtime/README.md @@ -0,0 +1,2 @@ +# sidechain dependency graph +cargo depgraph --features dcap,sidechain --include enclave-runtime,itp-types,ita-stf | dot -Tsvg > dependency-graph.svg diff --git a/enclave-runtime/src/initialization/global_components.rs b/enclave-runtime/src/initialization/global_components.rs index 1c5b67d9d3..e051b947e2 100644 --- a/enclave-runtime/src/initialization/global_components.rs +++ b/enclave-runtime/src/initialization/global_components.rs @@ -19,7 +19,6 @@ //! //! This allows the crates themselves to stay as generic as possible //! and ensures that the global instances are initialized once. - use crate::{ initialization::parentchain::{ integritee_parachain::IntegriteeParachainHandler, @@ -31,6 +30,7 @@ use crate::{ rpc::rpc_response_channel::RpcResponseChannel, tls_ra::seal_handler::SealHandler, }; +use ita_parentchain_interface::{integritee, target_a, target_b}; use ita_sgx_runtime::Runtime; use ita_stf::{Getter, State as StfState, Stf, TrustedCallSigned}; use itc_direct_rpc_server::{ @@ -43,13 +43,7 @@ use itc_parentchain::{ BlockImportDispatcher, }, block_importer::ParentchainBlockImporter, - indirect_calls_executor::{ - filter_metadata::{ - EventCreator, ShieldFundsAndInvokeFilter, TransferToAliceShieldsFundsFilter, - }, - parentchain_parser::ParentchainExtrinsicParser, - IndirectCallsExecutor, - }, + indirect_calls_executor::{filter_metadata::EventCreator, IndirectCallsExecutor}, light_client::{ concurrent_access::ValidatorAccessor, io::LightClientStateSealSync, light_validation::LightValidation, light_validation_state::LightValidationState, @@ -152,18 +146,6 @@ pub type EnclaveLightClientSeal = pub type EnclaveExtrinsicsFactory = ExtrinsicsFactory; -/// The enclave's generic indirect executor type. -/// -/// The `IndirectCallsFilter` calls filter can be configured per parentchain. -pub type EnclaveIndirectCallsExecutor = IndirectCallsExecutor< - EnclaveShieldingKeyRepository, - EnclaveStfEnclaveSigner, - EnclaveTopPoolAuthor, - EnclaveNodeMetadataRepository, - IndirectCallsFilter, - EventCreator, ->; - pub type EnclaveValidatorAccessor = ValidatorAccessor< LightValidation, ParentchainBlock, @@ -179,15 +161,24 @@ pub type EnclaveParentchainEventImportQueue = ImportQueue>; // Stuff for the integritee parentchain -pub type IntegriteeParentchainIndirectExecutor = - EnclaveIndirectCallsExecutor>; +pub type IntegriteeParentchainIndirectCallsExecutor = IndirectCallsExecutor< + EnclaveShieldingKeyRepository, + EnclaveStfEnclaveSigner, + EnclaveTopPoolAuthor, + EnclaveNodeMetadataRepository, + integritee::ShieldFundsAndInvokeFilter, + EventCreator, + integritee::ParentchainEventHandler, + EnclaveTrustedCallSigned, + EnclaveGetter, +>; pub type IntegriteeParentchainBlockImporter = ParentchainBlockImporter< ParentchainBlock, EnclaveValidatorAccessor, EnclaveStfExecutor, EnclaveExtrinsicsFactory, - IntegriteeParentchainIndirectExecutor, + IntegriteeParentchainIndirectCallsExecutor, >; pub type IntegriteeParentchainTriggeredBlockImportDispatcher = TriggeredDispatcher< @@ -212,15 +203,24 @@ pub type IntegriteeParentchainBlockImportDispatcher = BlockImportDispatcher< /// /// Also note that the extrinsic parser must be changed if the signed extra contains the /// `AssetTxPayment`. -pub type TargetAParentchainIndirectExecutor = - EnclaveIndirectCallsExecutor>; +pub type TargetAParentchainIndirectCallsExecutor = IndirectCallsExecutor< + EnclaveShieldingKeyRepository, + EnclaveStfEnclaveSigner, + EnclaveTopPoolAuthor, + EnclaveNodeMetadataRepository, + target_a::TransferToAliceShieldsFundsFilter, + EventCreator, + target_a::ParentchainEventHandler, + EnclaveTrustedCallSigned, + EnclaveGetter, +>; pub type TargetAParentchainBlockImporter = ParentchainBlockImporter< ParentchainBlock, EnclaveValidatorAccessor, EnclaveStfExecutor, EnclaveExtrinsicsFactory, - TargetAParentchainIndirectExecutor, + TargetAParentchainIndirectCallsExecutor, >; pub type TargetAParentchainTriggeredBlockImportDispatcher = TriggeredDispatcher< @@ -245,15 +245,24 @@ pub type TargetAParentchainBlockImportDispatcher = BlockImportDispatcher< /// /// Also note that the extrinsic parser must be changed if the signed extra contains the /// `AssetTxPayment`. -pub type TargetBParentchainIndirectExecutor = - EnclaveIndirectCallsExecutor>; +pub type TargetBParentchainIndirectCallsExecutor = IndirectCallsExecutor< + EnclaveShieldingKeyRepository, + EnclaveStfEnclaveSigner, + EnclaveTopPoolAuthor, + EnclaveNodeMetadataRepository, + target_b::TargetBExtrinsicFilter, + EventCreator, + target_b::ParentchainEventHandler, + EnclaveTrustedCallSigned, + EnclaveGetter, +>; pub type TargetBParentchainBlockImporter = ParentchainBlockImporter< ParentchainBlock, EnclaveValidatorAccessor, EnclaveStfExecutor, EnclaveExtrinsicsFactory, - TargetBParentchainIndirectExecutor, + TargetBParentchainIndirectCallsExecutor, >; pub type TargetBParentchainTriggeredBlockImportDispatcher = TriggeredDispatcher< diff --git a/enclave-runtime/src/initialization/parentchain/common.rs b/enclave-runtime/src/initialization/parentchain/common.rs index c2b6abea20..077a59217e 100644 --- a/enclave-runtime/src/initialization/parentchain/common.rs +++ b/enclave-runtime/src/initialization/parentchain/common.rs @@ -24,15 +24,16 @@ use crate::{ EnclaveParentchainSigner, EnclaveStfExecutor, EnclaveValidatorAccessor, IntegriteeParentchainBlockImportDispatcher, IntegriteeParentchainBlockImporter, IntegriteeParentchainImmediateBlockImportDispatcher, - IntegriteeParentchainIndirectExecutor, + IntegriteeParentchainIndirectCallsExecutor, IntegriteeParentchainTriggeredBlockImportDispatcher, TargetAParentchainBlockImportDispatcher, TargetAParentchainBlockImporter, - TargetAParentchainImmediateBlockImportDispatcher, TargetAParentchainIndirectExecutor, - TargetBParentchainBlockImportDispatcher, TargetBParentchainBlockImporter, - TargetBParentchainImmediateBlockImportDispatcher, TargetBParentchainIndirectExecutor, - GLOBAL_OCALL_API_COMPONENT, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, - GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, - GLOBAL_STATE_OBSERVER_COMPONENT, GLOBAL_TOP_POOL_AUTHOR_COMPONENT, + TargetAParentchainImmediateBlockImportDispatcher, + TargetAParentchainIndirectCallsExecutor, TargetBParentchainBlockImportDispatcher, + TargetBParentchainBlockImporter, TargetBParentchainImmediateBlockImportDispatcher, + TargetBParentchainIndirectCallsExecutor, GLOBAL_OCALL_API_COMPONENT, + GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, + GLOBAL_STATE_HANDLER_COMPONENT, GLOBAL_STATE_OBSERVER_COMPONENT, + GLOBAL_TOP_POOL_AUTHOR_COMPONENT, }, EnclaveStfEnclaveSigner, }, @@ -61,7 +62,7 @@ pub(crate) fn create_integritee_parentchain_block_importer( shielding_key_repository.clone(), top_pool_author.clone(), )); - let indirect_calls_executor = Arc::new(IntegriteeParentchainIndirectExecutor::new( + let indirect_calls_executor = Arc::new(IntegriteeParentchainIndirectCallsExecutor::new( shielding_key_repository, stf_enclave_signer, top_pool_author, @@ -92,7 +93,7 @@ pub(crate) fn create_target_a_parentchain_block_importer( shielding_key_repository.clone(), top_pool_author.clone(), )); - let indirect_calls_executor = Arc::new(TargetAParentchainIndirectExecutor::new( + let indirect_calls_executor = Arc::new(TargetAParentchainIndirectCallsExecutor::new( shielding_key_repository, stf_enclave_signer, top_pool_author, @@ -123,7 +124,7 @@ pub(crate) fn create_target_b_parentchain_block_importer( shielding_key_repository.clone(), top_pool_author.clone(), )); - let indirect_calls_executor = Arc::new(TargetBParentchainIndirectExecutor::new( + let indirect_calls_executor = Arc::new(TargetBParentchainIndirectCallsExecutor::new( shielding_key_repository, stf_enclave_signer, top_pool_author, diff --git a/enclave-runtime/src/test/top_pool_tests.rs b/enclave-runtime/src/test/top_pool_tests.rs index 32598e049f..9e9eee6217 100644 --- a/enclave-runtime/src/test/top_pool_tests.rs +++ b/enclave-runtime/src/test/top_pool_tests.rs @@ -29,14 +29,13 @@ use crate::test::{ }, }; use codec::Encode; +use ita_parentchain_interface::integritee; use ita_stf::{ test_genesis::{endowed_account, unendowed_account}, Getter, TrustedCall, TrustedCallSigned, }; use itc_parentchain::indirect_calls_executor::{ - filter_metadata::{ShieldFundsAndInvokeFilter, TestEventCreator}, - parentchain_parser::ParentchainExtrinsicParser, - ExecuteIndirectCalls, IndirectCallsExecutor, + mock::TestEventCreator, ExecuteIndirectCalls, IndirectCallsExecutor, }; use itc_parentchain_test::{ParentchainBlockBuilder, ParentchainHeaderBuilder}; use itp_node_api::{ @@ -135,8 +134,11 @@ pub fn submit_shielding_call_to_top_pool() { _, _, _, - ShieldFundsAndInvokeFilter, + integritee::ShieldFundsAndInvokeFilter, TestEventCreator, + integritee::ParentchainEventHandler, + TrustedCallSigned, + Getter, >::new( shielding_key_repo, enclave_signer, top_pool_author.clone(), node_meta_data_repository ); diff --git a/service/src/parentchain_handler.rs b/service/src/parentchain_handler.rs index 8b11e92b87..f27b603337 100644 --- a/service/src/parentchain_handler.rs +++ b/service/src/parentchain_handler.rs @@ -227,6 +227,7 @@ where while last_synced_header.number() < until_header.number() { last_synced_header = self.sync_parentchain(last_synced_header)?; trace!("[{:?}] synced block number: {}", id, last_synced_header.number); + std::thread::sleep(std::time::Duration::from_secs(1)); } self.trigger_parentchain_block_import()?; diff --git a/sidechain/consensus/common/Cargo.toml b/sidechain/consensus/common/Cargo.toml index 226f19d8d3..25fc717eed 100644 --- a/sidechain/consensus/common/Cargo.toml +++ b/sidechain/consensus/common/Cargo.toml @@ -64,8 +64,6 @@ std = [ "fork-tree/std", # substrate "sp-runtime/std", - # scs - "itp-types/std", ] sgx = [ "sgx_tstd",