Skip to content

Commit 67cabc4

Browse files
zhizming-zhongjingleizhangKailai-WangBillyWooo
authored
Check extrinsic status in indirect_calls_executor (#1193)
* try to resolve #1092 * move `ParentchainUncheckedExtrinsicWithStatus` to itp_types * revert update * revert update * update * done * fix unit tests * add unit tests * minor update * fmt * fix unit test * add script pre-commit.sh * update log level * minor update * update comment --------- Co-authored-by: ericzhang <jingleizhang@users.noreply.github.com> Co-authored-by: Kai <7630809+Kailai-Wang@users.noreply.github.com> Co-authored-by: BillyWooo <thedreamofbilly@gmail.com>
1 parent cbdc9eb commit 67cabc4

File tree

15 files changed

+339
-18
lines changed

15 files changed

+339
-18
lines changed

scripts/pre-commit.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env bash
2+
# It is recommended to execute the script before commit
3+
# which will help us to reduce test/fmt/clippy failures in CI
4+
5+
start=$(date +%s)
6+
7+
make fmt
8+
taplo fmt
9+
10+
root_dir=$(git rev-parse --show-toplevel)
11+
CARGO_TARGET_DIR=${root_dir}/target
12+
13+
cd "${root_dir}/tee-worker" || exit
14+
taplo fmt
15+
CARGO_TARGET_DIR=${CARGO_TARGET_DIR} cargo clippy --release -- -D warnings || exit
16+
17+
cd "${root_dir}/tee-worker/enclave-runtime" || exit
18+
taplo fmt
19+
CARGO_TARGET_DIR=${CARGO_TARGET_DIR} cargo clippy --release -- -D warnings || exit
20+
21+
cd "${root_dir}/tee-worker" || exit
22+
#RUST_LOG=info CARGO_TARGET_DIR=/root/work/tmp SKIP_WASM_BUILD=1 cargo test --release -- --show-output
23+
RUST_LOG=info CARGO_TARGET_DIR=${CARGO_TARGET_DIR} SKIP_WASM_BUILD=1 cargo test --release -- --show-output
24+
SGX_MODE=SW SKIP_WASM_BUILD=1 make
25+
26+
cd "${root_dir}/tee-worker/bin" || exit
27+
./integritee-service test --all
28+
29+
end=$(date +%s)
30+
echo "Elapsed Time: $((end-start)) seconds"

tee-worker/Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tee-worker/core-primitives/node-api/api-client-extensions/src/chain.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use itp_types::{Header, SignedBlock};
2020
use sp_core::{storage::StorageKey, Pair, H256};
2121
use sp_finality_grandpa::{AuthorityList, VersionedAuthorityList, GRANDPA_AUTHORITIES_KEY};
2222
use sp_runtime::MultiSignature;
23-
use substrate_api_client::{Api, ExtrinsicParams, RpcClient};
23+
use substrate_api_client::{Api, Events, ExtrinsicParams, RpcClient};
2424

2525
pub type StorageProof = Vec<Vec<u8>>;
2626

@@ -37,6 +37,7 @@ pub trait ChainApi {
3737
fn is_grandpa_available(&self) -> ApiResult<bool>;
3838
fn grandpa_authorities(&self, hash: Option<H256>) -> ApiResult<AuthorityList>;
3939
fn grandpa_authorities_proof(&self, hash: Option<H256>) -> ApiResult<StorageProof>;
40+
fn events(&self, hash: Option<H256>) -> ApiResult<Events>;
4041
}
4142

4243
impl<P: Pair, Client: RpcClient, Params: ExtrinsicParams> ChainApi for Api<P, Client, Params>
@@ -99,4 +100,10 @@ where
99100
.map(|read_proof| read_proof.proof.into_iter().map(|bytes| bytes.0).collect())
100101
.unwrap_or_default())
101102
}
103+
104+
fn events(&self, at_block: Option<H256>) -> ApiResult<Events> {
105+
let storagekey = self.metadata.storage_value_key("System", "Events")?;
106+
let events_bytes = self.get_opaque_storage_by_key_hash(storagekey, at_block)?;
107+
Ok(Events::new(self.metadata.clone(), at_block.unwrap(), events_bytes.unwrap()))
108+
}
102109
}

tee-worker/core-primitives/types/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
1717
# local dependencies
1818
itp-sgx-runtime-primitives = { path = "../../core-primitives/sgx-runtime-primitives", default-features = false }
1919

20+
# scs
21+
substrate-api-client = { default-features = false, git = "https://github.com/scs/substrate-api-client.git", branch = "polkadot-v0.9.36" }
22+
2023
# substrate-deps
2124
frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.36" }
2225
pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.36" }
@@ -27,6 +30,12 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substr
2730
# litentry
2831
litentry-primitives = { path = "../../litentry/primitives", default-features = false }
2932

33+
[dev-dependencies]
34+
sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.36" }
35+
sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.36" }
36+
substrate-api-client = { git = "https://github.com/scs/substrate-api-client.git", branch = "polkadot-v0.9.36" }
37+
itp-api-client-types = { path = "../../core-primitives/node-api/api-client-types" }
38+
3039
[features]
3140
default = ["std"]
3241
std = [
@@ -36,6 +45,7 @@ std = [
3645
"serde_json/std",
3746
"primitive-types/std",
3847
"itp-sgx-runtime-primitives/std",
48+
"substrate-api-client/std",
3949
# substrate
4050
"frame-system/std",
4151
"pallet-balances/std",
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// Copyright 2020-2023 Litentry Technologies GmbH.
2+
// This file is part of Litentry.
3+
//
4+
// Litentry is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// Litentry is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with Litentry. If not, see <https://www.gnu.org/licenses/>.
16+
17+
use codec::{Decode, Encode, Error, Input};
18+
use sp_runtime::OpaqueExtrinsic;
19+
use sp_std::vec::Vec;
20+
use substrate_api_client::{PlainTip, SubstrateDefaultSignedExtra, UncheckedExtrinsicV4};
21+
22+
/// Same function as in primitives::generic. Needed to be copied as it is private there.
23+
fn encode_with_vec_prefix<T: Encode, F: Fn(&mut Vec<u8>)>(encoder: F) -> Vec<u8> {
24+
let size = sp_std::mem::size_of::<T>();
25+
let reserve = match size {
26+
0..=0b0011_1111 => 1,
27+
0b0100_0000..=0b0011_1111_1111_1111 => 2,
28+
_ => 4,
29+
};
30+
let mut v = Vec::with_capacity(reserve + size);
31+
v.resize(reserve, 0);
32+
encoder(&mut v);
33+
34+
// need to prefix with the total length to ensure it's binary compatible with
35+
// Vec<u8>.
36+
let mut length: Vec<()> = Vec::new();
37+
length.resize(v.len() - reserve, ());
38+
length.using_encoded(|s| {
39+
v.splice(0..reserve, s.iter().cloned());
40+
});
41+
42+
v
43+
}
44+
45+
#[derive(PartialEq, Eq, Clone, Debug)]
46+
pub struct OpaqueExtrinsicWithStatus {
47+
pub xt: OpaqueExtrinsic,
48+
pub status: bool,
49+
}
50+
51+
impl Encode for OpaqueExtrinsicWithStatus {
52+
fn encode(&self) -> Vec<u8> {
53+
encode_with_vec_prefix::<Self, _>(|v| {
54+
self.xt.encode_to(v);
55+
self.status.encode_to(v);
56+
})
57+
}
58+
}
59+
60+
#[derive(Clone, Eq, PartialEq, Debug)]
61+
pub struct ParentchainUncheckedExtrinsicWithStatus<Call> {
62+
pub xt: UncheckedExtrinsicV4<Call, SubstrateDefaultSignedExtra<PlainTip>>,
63+
pub status: bool,
64+
}
65+
66+
impl<Call> Decode for ParentchainUncheckedExtrinsicWithStatus<Call>
67+
where
68+
UncheckedExtrinsicV4<Call, SubstrateDefaultSignedExtra<PlainTip>>: Decode,
69+
{
70+
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
71+
// This is a little more complicated than usual since the binary format must be compatible
72+
// with substrate's generic `Vec<u8>` type. Basically this just means accepting that there
73+
// will be a prefix of vector length (we don't need
74+
// to use this).
75+
let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?;
76+
77+
Ok(ParentchainUncheckedExtrinsicWithStatus::<Call> {
78+
xt: Decode::decode(input)?,
79+
status: Decode::decode(input)?,
80+
})
81+
}
82+
}
83+
84+
pub fn fill_opaque_extrinsic_with_status(
85+
opaque_extrinsic: OpaqueExtrinsic,
86+
status: bool,
87+
) -> Result<OpaqueExtrinsic, codec::Error> {
88+
let opaque_extrinsic_with_status = OpaqueExtrinsicWithStatus { xt: opaque_extrinsic, status };
89+
OpaqueExtrinsic::from_bytes(opaque_extrinsic_with_status.encode().as_slice())
90+
}
91+
92+
// #[cfg(test)]
93+
// mod tests {
94+
// use crate::extrinsics::OpaqueExtrinsicWithStatus;
95+
// use itp_api_client_types::ParentchainUncheckedExtrinsicWithStatus;
96+
// use sp_core::{hexdisplay::HexDisplay, Pair, H256 as Hash};
97+
// use sp_runtime::{generic::Era, testing::sr25519, MultiSignature, OpaqueExtrinsic};
98+
// use substrate_api_client::{
99+
// BaseExtrinsicParams, PlainTip, PlainTipExtrinsicParams, PlainTipExtrinsicParamsBuilder,
100+
// SubstrateDefaultSignedExtra, UncheckedExtrinsicV4,
101+
// };
102+
//
103+
// #[test]
104+
// fn encode_decode_works() {
105+
// let msg = &b"test-message"[..];
106+
// let (pair, _) = sr25519::Pair::generate();
107+
// let signature = pair.sign(msg);
108+
// let multi_sig = MultiSignature::from(signature);
109+
// let account: AccountId = pair.public().into();
110+
// let tx_params =
111+
// PlainTipExtrinsicParamsBuilder::new().era(Era::mortal(8, 0), Hash::from([0u8; 32]));
112+
//
113+
// let default_extra = BaseExtrinsicParams::new(0, 0, 2, Hash::from([0u8; 32]), tx_params);
114+
// let xt = UncheckedExtrinsicV4::new_signed(
115+
// vec![1, 1, 1],
116+
// account.into(),
117+
// multi_sig,
118+
// default_extra.signed_extra(),
119+
// );
120+
// let unchecked_with_status =
121+
// ParentchainUncheckedExtrinsicWithStatus { xt: xt.clone(), status: true };
122+
// let op_xt = OpaqueExtrinsic::from_bytes(xt.encode().as_slice()).unwrap();
123+
// let op_xt_with_status = OpaqueExtrinsicWithStatus { raw: op_xt, status: true };
124+
// assert_eq!(with_status, Decode::decode(&mut op_xt_with_status.encode().as_slice()).unwrap())
125+
// }
126+
// }

tee-worker/core-primitives/types/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use codec::{Decode, Encode};
2424
use sgx_tstd as std;
2525
use sp_std::vec::Vec;
2626

27+
pub mod extrinsics;
2728
pub mod storage;
2829

2930
/// Substrate runtimes provide no string type. Hence, for arbitrary data of varying length the

tee-worker/core/parentchain/indirect-calls-executor/src/executor/mod.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use itp_node_api::{
2626
use itp_sgx_crypto::{key_repository::AccessKey, ShieldingCryptoDecrypt, ShieldingCryptoEncrypt};
2727
use itp_stf_executor::traits::StfEnclaveSigning;
2828
use itp_top_pool_author::traits::AuthorApi;
29-
use itp_types::H256;
29+
use itp_types::{extrinsics::ParentchainUncheckedExtrinsicWithStatus, H256};
3030

3131
pub mod call_worker;
3232
pub mod litentry;
@@ -67,8 +67,8 @@ pub(crate) trait Executor<
6767
fn decode(
6868
&self,
6969
input: &mut &[u8],
70-
) -> Result<ParentchainUncheckedExtrinsic<Self::Call>, CodecError> {
71-
ParentchainUncheckedExtrinsic::<Self::Call>::decode(input)
70+
) -> Result<ParentchainUncheckedExtrinsicWithStatus<Self::Call>, CodecError> {
71+
ParentchainUncheckedExtrinsicWithStatus::<Self::Call>::decode(input)
7272
}
7373

7474
/// extrinisc in this function should execute successfully on parentchain
@@ -126,10 +126,18 @@ where
126126
>,
127127
input: &mut &[u8],
128128
) -> Result<ExecutionStatus<H256>, Error> {
129-
if let Ok(xt) = self.decode(input) {
129+
if let Ok(ParentchainUncheckedExtrinsicWithStatus { xt, status }) = self.decode(input) {
130130
if self.is_target_call(&xt.function, context.node_meta_data_provider.as_ref()) {
131-
self.execute(context, xt.clone())
132-
.map(|_| ExecutionStatus::Success(hash_of(&xt)))
131+
if status {
132+
self.execute(context, xt.clone())
133+
.map(|_| ExecutionStatus::Success(hash_of(&xt)))
134+
} else {
135+
log::warn!(
136+
"extrinsic(call index: {:?}) fail to execute on parentchain.",
137+
self.call_index(&xt.function)
138+
);
139+
Ok(ExecutionStatus::Skip)
140+
}
133141
} else {
134142
Ok(ExecutionStatus::NextExecutor)
135143
}

tee-worker/core/parentchain/indirect-calls-executor/src/lib.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ use std::{sync::Arc, vec, vec::Vec};
7171
pub enum ExecutionStatus<R> {
7272
Success(R),
7373
NextExecutor,
74+
Skip,
7475
}
7576

7677
/// Trait to execute the indirect calls found in the extrinsics of a block.
@@ -233,6 +234,7 @@ impl<ShieldingKeyRepository, StfEnclaveSigner, TopPoolAuthor, NodeMetadataProvid
233234
break
234235
},
235236
Ok(ExecutionStatus::NextExecutor) => continue,
237+
Ok(ExecutionStatus::Skip) => break,
236238
Err(e) => {
237239
log::warn!("fail to execute indirect_call. due to {:?} ", e);
238240
match e {
@@ -296,7 +298,10 @@ mod test {
296298
use itp_stf_primitives::types::AccountId;
297299
use itp_test::mock::shielding_crypto_mock::ShieldingCryptoMock;
298300
use itp_top_pool_author::mocks::AuthorApiMock;
299-
use itp_types::{Block, CallWorkerFn, Request, ShardIdentifier, ShieldFundsFn};
301+
use itp_types::{
302+
extrinsics::fill_opaque_extrinsic_with_status, Block, CallWorkerFn, Request,
303+
ShardIdentifier, ShieldFundsFn,
304+
};
300305
use sp_core::{ed25519, Pair};
301306
use sp_runtime::{MultiSignature, OpaqueExtrinsic};
302307
use std::assert_matches::assert_matches;
@@ -328,7 +333,9 @@ mod test {
328333
.unwrap();
329334

330335
let parentchain_block = ParentchainBlockBuilder::default()
331-
.with_extrinsics(vec![opaque_extrinsic])
336+
.with_extrinsics(vec![
337+
fill_opaque_extrinsic_with_status(opaque_extrinsic, true).unwrap()
338+
])
332339
.build();
333340

334341
indirect_calls_executor
@@ -338,6 +345,30 @@ mod test {
338345
assert_eq!(1, top_pool_author.pending_tops(shard_id()).unwrap().len());
339346
}
340347

348+
#[test]
349+
fn failed_indirect_call_is_skipped() {
350+
let _ = env_logger::builder().is_test(true).try_init();
351+
352+
let (indirect_calls_executor, top_pool_author, _) =
353+
test_fixtures([0u8; 32], NodeMetadataMock::new());
354+
355+
let opaque_extrinsic =
356+
OpaqueExtrinsic::from_bytes(call_worker_unchecked_extrinsic().encode().as_slice())
357+
.unwrap();
358+
359+
let parentchain_block = ParentchainBlockBuilder::default()
360+
.with_extrinsics(vec![
361+
fill_opaque_extrinsic_with_status(opaque_extrinsic, false).unwrap()
362+
])
363+
.build();
364+
365+
indirect_calls_executor
366+
.execute_indirect_calls_in_extrinsics(&parentchain_block)
367+
.unwrap();
368+
369+
assert_eq!(0, top_pool_author.pending_tops(shard_id()).unwrap().len());
370+
}
371+
341372
#[test]
342373
fn shielding_call_can_be_added_to_pool_successfully() {
343374
let _ = env_logger::builder().is_test(true).try_init();
@@ -353,7 +384,9 @@ mod test {
353384
.unwrap();
354385

355386
let parentchain_block = ParentchainBlockBuilder::default()
356-
.with_extrinsics(vec![opaque_extrinsic])
387+
.with_extrinsics(vec![
388+
fill_opaque_extrinsic_with_status(opaque_extrinsic, true).unwrap()
389+
])
357390
.build();
358391

359392
indirect_calls_executor

tee-worker/enclave-runtime/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)