diff --git a/common/primitives/core/src/intention.rs b/common/primitives/core/src/intent.rs similarity index 97% rename from common/primitives/core/src/intention.rs rename to common/primitives/core/src/intent.rs index 2255c1eabd..10e01a2344 100644 --- a/common/primitives/core/src/intention.rs +++ b/common/primitives/core/src/intent.rs @@ -6,7 +6,7 @@ use sp_runtime::{traits::ConstU32, BoundedVec}; pub const CALL_ETHEREUM_INPUT_LEN: u32 = 10 * 1024; #[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, MaxEncodedLen, TypeInfo)] -pub enum Intention { +pub enum Intent { #[codec(index = 0)] TransferEthereum(TransferEthereum), #[codec(index = 1)] diff --git a/common/primitives/core/src/lib.rs b/common/primitives/core/src/lib.rs index daf96583e5..90537c6b07 100644 --- a/common/primitives/core/src/lib.rs +++ b/common/primitives/core/src/lib.rs @@ -32,8 +32,8 @@ pub use assertion::Assertion; pub mod identity; pub use identity::*; -pub mod intention; -pub use intention::*; +pub mod intent; +pub use intent::*; pub mod omni_account; pub use omni_account::*; diff --git a/parachain/pallets/omni-account/src/lib.rs b/parachain/pallets/omni-account/src/lib.rs index 4346b61b1c..988c0b69a3 100644 --- a/parachain/pallets/omni-account/src/lib.rs +++ b/parachain/pallets/omni-account/src/lib.rs @@ -21,7 +21,7 @@ mod mock; #[cfg(test)] mod tests; -pub use core_primitives::{Identity, Intention, MemberAccount, OmniAccountConverter}; +pub use core_primitives::{Identity, Intent, MemberAccount, OmniAccountConverter}; pub use frame_system::pallet_prelude::BlockNumberFor; pub use pallet::*; @@ -55,6 +55,13 @@ pub enum RawOrigin { pub mod pallet { use super::*; + #[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo)] + pub enum IntentExecutionResult { + Success, + Failure, + } + + /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); #[pallet::pallet] @@ -97,6 +104,8 @@ pub mod pallet { /// Convert an `Identity` to OmniAccount type type OmniAccountConverter: OmniAccountConverter; + + type SetOmniExecutorOrigin: EnsureOrigin<::RuntimeOrigin>; } pub type MemberAccounts = BoundedVec::MaxAccountStoreLength>; @@ -115,6 +124,10 @@ pub mod pallet { pub type MemberAccountHash = StorageMap; + #[pallet::storage] + #[pallet::getter(fn omni_executor)] + pub type OmniExecutor = StorageValue<_, T::AccountId, OptionQuery>; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -132,8 +145,12 @@ pub mod pallet { DispatchedAsOmniAccount { who: T::AccountId, result: DispatchResult }, /// Some call is dispatched as signed origin DispatchedAsSigned { who: T::AccountId, result: DispatchResult }, - /// Intention is requested - IntentionRequested { who: T::AccountId, intention: Intention }, + /// Intent is requested + IntentRequested { who: T::AccountId, intent: Intent }, + /// Intent is executed + IntentExecuted { who: T::AccountId, intent: Intent, result: IntentExecutionResult }, + /// Omni executor is set + OmniExecutorSet { omni_executor: T::AccountId }, } #[pallet::error] @@ -144,6 +161,7 @@ pub mod pallet { InvalidAccount, UnknownAccountStore, EmptyAccount, + RequireOmniExecutor, } #[pallet::call] @@ -290,9 +308,9 @@ pub mod pallet { #[pallet::call_index(6)] #[pallet::weight((195_000_000, DispatchClass::Normal))] - pub fn request_intention(origin: OriginFor, intention: Intention) -> DispatchResult { + pub fn request_intent(origin: OriginFor, intent: Intent) -> DispatchResult { let who = T::OmniAccountOrigin::ensure_origin(origin)?; - Self::deposit_event(Event::IntentionRequested { who, intention }); + Self::deposit_event(Event::IntentRequested { who, intent }); Ok(()) } @@ -325,6 +343,31 @@ pub mod pallet { Ok(Pays::No.into()) } + + #[pallet::call_index(8)] + #[pallet::weight((195_000_000, DispatchClass::Normal, Pays::No))] + pub fn intent_executed( + origin: OriginFor, + who: T::AccountId, + intent: Intent, + result: IntentExecutionResult, + ) -> DispatchResult { + Self::ensure_omni_executor(origin)?; + Self::deposit_event(Event::IntentExecuted { who, intent, result }); + Ok(()) + } + + #[pallet::call_index(9)] + #[pallet::weight((195_000_000, DispatchClass::Normal, Pays::No))] + pub fn set_omni_executor( + origin: OriginFor, + new_omni_executor: T::AccountId, + ) -> DispatchResult { + T::SetOmniExecutorOrigin::ensure_origin(origin)?; + OmniExecutor::::put(new_omni_executor.clone()); + Self::deposit_event(Event::OmniExecutorSet { omni_executor: new_omni_executor }); + Ok(()) + } } impl Pallet { @@ -346,6 +389,14 @@ pub mod pallet { Ok(member_accounts) } + + fn ensure_omni_executor(origin: OriginFor) -> DispatchResult { + ensure!( + Some(ensure_signed(origin)?) == Self::omni_executor(), + Error::::RequireOmniExecutor + ); + Ok(()) + } } } diff --git a/parachain/pallets/omni-account/src/mock.rs b/parachain/pallets/omni-account/src/mock.rs index 81fe2067f4..df7105bca7 100644 --- a/parachain/pallets/omni-account/src/mock.rs +++ b/parachain/pallets/omni-account/src/mock.rs @@ -187,6 +187,7 @@ impl pallet_omni_account::Config for TestRuntime { type MaxAccountStoreLength = ConstU32<3>; type OmniAccountOrigin = EnsureOmniAccount; type OmniAccountConverter = DefaultOmniAccountConverter; + type SetOmniExecutorOrigin = EnsureRoot; } pub fn get_tee_signer() -> SystemAccountId { diff --git a/parachain/pallets/omni-account/src/tests.rs b/parachain/pallets/omni-account/src/tests.rs index 642e2b0d03..7c42cc01bf 100644 --- a/parachain/pallets/omni-account/src/tests.rs +++ b/parachain/pallets/omni-account/src/tests.rs @@ -38,8 +38,8 @@ fn publicize_account_call(id: Identity) -> Box { Box::new(call) } -fn request_intention_call(intention: Intention) -> Box { - RuntimeCall::OmniAccount(crate::Call::request_intention { intention }).into() +fn request_intent_call(intent: Intent) -> Box { + RuntimeCall::OmniAccount(crate::Call::request_intent { intent }).into() } fn make_balance_transfer_call(dest: AccountId, value: Balance) -> Box { @@ -498,7 +498,7 @@ fn publicize_account_identity_not_found_works() { } #[test] -fn request_intention_works() { +fn request_intent_works() { new_test_ext().execute_with(|| { let tee_signer = get_tee_signer(); let bob = private_member_account(bob()); @@ -515,12 +515,10 @@ fn request_intention_works() { call )); - let intention = Intention::CallEthereum(CallEthereum { - address: H160::zero(), - input: BoundedVec::new(), - }); + let intent = + Intent::CallEthereum(CallEthereum { address: H160::zero(), input: BoundedVec::new() }); - let call = request_intention_call(intention.clone()); + let call = request_intent_call(intent.clone()); assert_ok!(OmniAccount::dispatch_as_omni_account( RuntimeOrigin::signed(tee_signer.clone()), alice().identity.hash(), @@ -536,7 +534,7 @@ fn request_intention_works() { ); System::assert_has_event( - Event::IntentionRequested { who: alice().omni_account, intention }.into(), + Event::IntentRequested { who: alice().omni_account, intent }.into(), ); }); } diff --git a/parachain/runtime/litentry/src/lib.rs b/parachain/runtime/litentry/src/lib.rs index 928c587909..8280bb8252 100644 --- a/parachain/runtime/litentry/src/lib.rs +++ b/parachain/runtime/litentry/src/lib.rs @@ -959,6 +959,7 @@ impl pallet_omni_account::Config for Runtime { type MaxAccountStoreLength = ConstU32<64>; type OmniAccountOrigin = EnsureOmniAccount; type OmniAccountConverter = DefaultOmniAccountConverter; + type SetOmniExecutorOrigin = EnsureRootOrHalfCouncil; } impl pallet_bitacross::Config for Runtime { diff --git a/parachain/runtime/paseo/src/lib.rs b/parachain/runtime/paseo/src/lib.rs index ade5674e0d..32440c7506 100644 --- a/parachain/runtime/paseo/src/lib.rs +++ b/parachain/runtime/paseo/src/lib.rs @@ -1002,6 +1002,7 @@ impl pallet_omni_account::Config for Runtime { type MaxAccountStoreLength = ConstU32<64>; type OmniAccountOrigin = EnsureOmniAccount; type OmniAccountConverter = DefaultOmniAccountConverter; + type SetOmniExecutorOrigin = EnsureRootOrAllCouncil; } impl pallet_bitacross::Config for Runtime { diff --git a/parachain/runtime/rococo/src/lib.rs b/parachain/runtime/rococo/src/lib.rs index 76a1167de7..5809502b60 100644 --- a/parachain/runtime/rococo/src/lib.rs +++ b/parachain/runtime/rococo/src/lib.rs @@ -1001,6 +1001,7 @@ impl pallet_omni_account::Config for Runtime { type MaxAccountStoreLength = ConstU32<64>; type OmniAccountOrigin = EnsureOmniAccount; type OmniAccountConverter = DefaultOmniAccountConverter; + type SetOmniExecutorOrigin = EnsureRootOrAllCouncil; } impl pallet_bitacross::Config for Runtime { diff --git a/tee-worker/omni-executor/Cargo.lock b/tee-worker/omni-executor/Cargo.lock index 96aca827b5..d2368f28f5 100644 --- a/tee-worker/omni-executor/Cargo.lock +++ b/tee-worker/omni-executor/Cargo.lock @@ -993,6 +993,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ "bitcoin_hashes", + "serde", + "unicode-normalization", ] [[package]] @@ -1748,7 +1750,7 @@ dependencies = [ "env_logger", "executor-core", "hex", - "intention-executor", + "intent-executor", "log", "parentchain-listener", "scale-encode", @@ -2012,6 +2014,7 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9" dependencies = [ + "rand", "rand_core", ] @@ -2386,7 +2389,7 @@ dependencies = [ ] [[package]] -name = "intention-executor" +name = "intent-executor" version = "0.1.0" dependencies = [ "alloy", @@ -3004,10 +3007,13 @@ dependencies = [ "async-trait", "env_logger", "executor-core", + "hex", "log", "parity-scale-codec", "scale-encode", "subxt", + "subxt-core", + "subxt-signer", "tokio", ] @@ -3066,6 +3072,17 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" @@ -3079,6 +3096,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest 0.10.7", + "password-hash", ] [[package]] @@ -3956,6 +3974,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secp256k1" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +dependencies = [ + "cc", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -4524,6 +4569,28 @@ dependencies = [ "sp-crypto-hashing", ] +[[package]] +name = "subxt-signer" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49888ae6ae90fe01b471193528eea5bd4ed52d8eecd2d13f4a2333b87388850" +dependencies = [ + "bip39", + "cfg-if", + "hex", + "hmac 0.12.1", + "parity-scale-codec", + "pbkdf2", + "regex", + "schnorrkel", + "secp256k1", + "secrecy", + "sha2 0.10.8", + "sp-crypto-hashing", + "subxt-core", + "zeroize", +] + [[package]] name = "syn" version = "1.0.109" @@ -4855,6 +4922,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.7", + "rand", "static_assertions", ] @@ -4902,9 +4970,9 @@ checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.24" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] diff --git a/tee-worker/omni-executor/Cargo.toml b/tee-worker/omni-executor/Cargo.toml index a3135cf7c7..d951c26efa 100644 --- a/tee-worker/omni-executor/Cargo.toml +++ b/tee-worker/omni-executor/Cargo.toml @@ -3,7 +3,7 @@ members = [ "executor-core", "executor-worker", "parentchain/listener", - "ethereum/intention-executor", + "ethereum/intent-executor", ] resolver = "2" diff --git a/tee-worker/omni-executor/ethereum/intention-executor/Cargo.toml b/tee-worker/omni-executor/ethereum/intent-executor/Cargo.toml similarity index 92% rename from tee-worker/omni-executor/ethereum/intention-executor/Cargo.toml rename to tee-worker/omni-executor/ethereum/intent-executor/Cargo.toml index 97ecd9bc87..3bb21d68ea 100644 --- a/tee-worker/omni-executor/ethereum/intention-executor/Cargo.toml +++ b/tee-worker/omni-executor/ethereum/intent-executor/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "intention-executor" +name = "intent-executor" version = "0.1.0" authors = ['Trust Computing GmbH '] edition.workspace = true diff --git a/tee-worker/omni-executor/ethereum/intention-executor/src/lib.rs b/tee-worker/omni-executor/ethereum/intent-executor/src/lib.rs similarity index 83% rename from tee-worker/omni-executor/ethereum/intention-executor/src/lib.rs rename to tee-worker/omni-executor/ethereum/intent-executor/src/lib.rs index 9ae22727c4..e8b9efc162 100644 --- a/tee-worker/omni-executor/ethereum/intention-executor/src/lib.rs +++ b/tee-worker/omni-executor/ethereum/intent-executor/src/lib.rs @@ -22,25 +22,25 @@ use alloy::providers::{Provider, ProviderBuilder, WalletProvider}; use alloy::rpc::types::{TransactionInput, TransactionRequest}; use alloy::signers::local::PrivateKeySigner; use async_trait::async_trait; -use executor_core::intention_executor::IntentionExecutor; -use executor_core::primitives::Intention; +use executor_core::intent_executor::IntentExecutor; +use executor_core::primitives::Intent; use log::{error, info}; -/// Executes intentions on Ethereum network. -pub struct EthereumIntentionExecutor { +/// Executes intents on Ethereum network. +pub struct EthereumIntentExecutor { rpc_url: String, } -impl EthereumIntentionExecutor { +impl EthereumIntentExecutor { pub fn new(rpc_url: &str) -> Result { Ok(Self { rpc_url: rpc_url.to_string() }) } } #[async_trait] -impl IntentionExecutor for EthereumIntentionExecutor { - async fn execute(&self, intention: Intention) -> Result<(), ()> { - info!("Relaying intention: {:?}", intention); +impl IntentExecutor for EthereumIntentExecutor { + async fn execute(&self, intent: Intent) -> Result<(), ()> { + info!("Executin intent: {:?}", intent); // todo: this should be retrieved from key_store let signer = PrivateKeySigner::from_str( "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", @@ -53,8 +53,8 @@ impl IntentionExecutor for EthereumIntentionExecutor { .on_http(self.rpc_url.parse().map_err(|e| error!("Could not parse rpc url: {:?}", e))?); let account = provider.get_account(provider.signer_addresses().next().unwrap()).await.unwrap(); - match intention { - Intention::TransferEthereum(to, value) => { + match intent { + Intent::TransferEthereum(to, value) => { let tx = TransactionRequest::default() .to(Address::from(to)) .nonce(account.nonce) @@ -67,7 +67,7 @@ impl IntentionExecutor for EthereumIntentionExecutor { error!("Could not get transaction receipt: {:?}", e); })?; }, - Intention::CallEthereum(address, input) => { + Intent::CallEthereum(address, input) => { let tx = TransactionRequest::default() .to(Address::from(address)) .nonce(account.nonce) diff --git a/tee-worker/omni-executor/executor-core/src/intention_executor.rs b/tee-worker/omni-executor/executor-core/src/intent_executor.rs similarity index 73% rename from tee-worker/omni-executor/executor-core/src/intention_executor.rs rename to tee-worker/omni-executor/executor-core/src/intent_executor.rs index fd46337883..6436ad4f86 100644 --- a/tee-worker/omni-executor/executor-core/src/intention_executor.rs +++ b/tee-worker/omni-executor/executor-core/src/intent_executor.rs @@ -17,19 +17,19 @@ use async_trait::async_trait; use tokio::sync::mpsc; -use crate::primitives::Intention; +use crate::primitives::Intent; -/// Used to perform intention on destination chain +/// Used to perform intent on destination chain #[async_trait] -pub trait IntentionExecutor: Send { - async fn execute(&self, intention: Intention) -> Result<(), ()>; +pub trait IntentExecutor: Send { + async fn execute(&self, intent: Intent) -> Result<(), ()>; } -pub struct MockedIntentionExecutor { +pub struct MockedIntentExecutor { sender: mpsc::UnboundedSender<()>, } -impl MockedIntentionExecutor { +impl MockedIntentExecutor { pub fn new() -> (Self, mpsc::UnboundedReceiver<()>) { let (sender, receiver) = mpsc::unbounded_channel(); (Self { sender }, receiver) @@ -37,8 +37,8 @@ impl MockedIntentionExecutor { } #[async_trait] -impl IntentionExecutor for MockedIntentionExecutor { - async fn execute(&self, _intention: Intention) -> Result<(), ()> { +impl IntentExecutor for MockedIntentExecutor { + async fn execute(&self, _intent: Intent) -> Result<(), ()> { self.sender.send(()).map_err(|_| ()) } } diff --git a/tee-worker/omni-executor/executor-core/src/key_store.rs b/tee-worker/omni-executor/executor-core/src/key_store.rs new file mode 100644 index 0000000000..0e0932c0b6 --- /dev/null +++ b/tee-worker/omni-executor/executor-core/src/key_store.rs @@ -0,0 +1,49 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use log::error; +use std::fs; +use std::fs::File; +use std::io::Write; + +/// Used for persisting Relayer's keys. +pub trait KeyStore { + fn generate_key() -> Result; + fn serialize(k: &K) -> Result, ()>; + fn deserialize(sealed: Vec) -> Result; + fn path(&self) -> String; + fn read(&self) -> Result { + match fs::read(self.path()) { + Ok(content) => Self::deserialize(content), + Err(_) => Err(()), + } + } + fn write(&self, k: &K) -> Result<(), ()> { + match File::create(self.path()) { + Ok(mut file) => { + file.write( + &Self::serialize(k).map_err(|e| error!("Error writing to file: {:?}", e))?, + ) + .map_err(|_| ())?; + Ok(()) + }, + Err(e) => { + error!("Could not seal data to file {}: {:?}", self.path(), e); + Err(()) + }, + } + } +} diff --git a/tee-worker/omni-executor/executor-core/src/lib.rs b/tee-worker/omni-executor/executor-core/src/lib.rs index f12f2f081f..cc722f4831 100644 --- a/tee-worker/omni-executor/executor-core/src/lib.rs +++ b/tee-worker/omni-executor/executor-core/src/lib.rs @@ -16,7 +16,8 @@ pub mod event_handler; pub mod fetcher; -pub mod intention_executor; +pub mod intent_executor; +pub mod key_store; pub mod listener; pub mod primitives; pub mod sync_checkpoint_repository; diff --git a/tee-worker/omni-executor/executor-core/src/listener.rs b/tee-worker/omni-executor/executor-core/src/listener.rs index 76fc11d7c8..126c45e7a3 100644 --- a/tee-worker/omni-executor/executor-core/src/listener.rs +++ b/tee-worker/omni-executor/executor-core/src/listener.rs @@ -24,23 +24,10 @@ use crate::fetcher::{EventsFetcher, LastFinalizedBlockNumFetcher}; use crate::primitives::GetEventId; use crate::sync_checkpoint_repository::{Checkpoint, CheckpointRepository}; -/// Represents event emitted on listened chain. -#[derive(Clone, Debug, PartialEq)] -pub struct IntentionEvent { - id: Id, - //todo: fill -} - -impl IntentionEvent { - pub fn new(id: Id) -> Self { - Self { id } - } -} - -/// Component, used to listen to chain and execute requested intentions +/// Component, used to listen to chain and execute requested intents /// Requires specific implementations of: /// `Fetcher` - used to fetch data from chain -/// `IntentionExecutor` - used to execute intentions on target chain +/// `IntentExecutor` - used to execute intents on target chain /// `CheckpointRepository` - used to store listener's progress /// `EventId` - represents chain event id /// `BlockEvent` - represents chain event @@ -50,12 +37,12 @@ pub struct Listener< CheckpointRepository, BlockEventId, BlockEvent, - IntentionEventHandler, + IntentEventHandler, > { id: String, handle: Handle, fetcher: Fetcher, - intention_event_handler: IntentionEventHandler, + intent_event_handler: IntentEventHandler, stop_signal: Receiver<()>, checkpoint_repository: CheckpointRepository, _phantom: PhantomData<(Checkpoint, BlockEventId, BlockEvent)>, @@ -67,15 +54,14 @@ impl< Fetcher: LastFinalizedBlockNumFetcher + EventsFetcher, CheckpointT: PartialOrd + Checkpoint + From, CheckpointRepositoryT: CheckpointRepository, - IntentionEventHandler: EventHandler, - > - Listener + IntentEventHandler: EventHandler, + > Listener { pub fn new( id: &str, handle: Handle, fetcher: Fetcher, - intention_event_handler: IntentionEventHandler, + intent_event_handler: IntentEventHandler, stop_signal: Receiver<()>, last_processed_log_repository: CheckpointRepositoryT, ) -> Result { @@ -83,7 +69,7 @@ impl< id: id.to_string(), handle, fetcher, - intention_event_handler, + intent_event_handler, stop_signal, checkpoint_repository: last_processed_log_repository, _phantom: PhantomData, @@ -159,40 +145,51 @@ impl< self.checkpoint_repository.get().expect("Could not read checkpoint") { if checkpoint.lt(&event.get_event_id().clone().into()) { - log::info!("Executing intention"); + log::info!("Handling event: {:?}", event_id); if let Err(e) = - self.handle.block_on(self.intention_event_handler.handle(event)) + self.handle.block_on(self.intent_event_handler.handle(event)) { - log::error!("Could not execute intention: {:?}", e); + log::error!("Could not handle event: {:?}", e); match e { Error::NonRecoverableError => { - error!("Non-recoverable intention handling error, event: {:?}", event_id); + error!("Non-recoverable intent handling error, event: {:?}", event_id); break 'main; }, Error::RecoverableError => { error!( - "Recoverable intention handling error, event: {:?}", + "Recoverable intent handling error, event: {:?}", event_id ); continue 'main; }, } } - log::info!("Intention executed"); } else { log::debug!("Skipping event"); } } else { log::info!("Handling event: {:?}", event_id); if let Err(e) = - self.handle.block_on(self.intention_event_handler.handle(event)) + self.handle.block_on(self.intent_event_handler.handle(event)) { - log::error!("Could not execute intention: {:?}", e); - // sleep(Duration::from_secs(1)); - // // it will try again in next loop - // continue 'main; + log::error!("Could not handle event: {:?}", e); + match e { + Error::NonRecoverableError => { + error!( + "Non-recoverable intent handling error, event: {:?}", + event_id + ); + break 'main; + }, + Error::RecoverableError => { + error!( + "Recoverable intent handling error, event: {:?}", + event_id + ); + continue 'main; + }, + } } - log::info!("Intention executed"); } self.checkpoint_repository .save(event_id.into()) diff --git a/tee-worker/omni-executor/executor-core/src/primitives.rs b/tee-worker/omni-executor/executor-core/src/primitives.rs index 4dd12f93dc..8f707a8204 100644 --- a/tee-worker/omni-executor/executor-core/src/primitives.rs +++ b/tee-worker/omni-executor/executor-core/src/primitives.rs @@ -15,7 +15,7 @@ // along with Litentry. If not, see . #[derive(Debug)] -pub enum Intention { +pub enum Intent { TransferEthereum([u8; 20], [u8; 32]), CallEthereum([u8; 20], Vec), } diff --git a/tee-worker/omni-executor/executor-worker/Cargo.toml b/tee-worker/omni-executor/executor-worker/Cargo.toml index e4641b65e5..5ee9b46240 100644 --- a/tee-worker/omni-executor/executor-worker/Cargo.toml +++ b/tee-worker/omni-executor/executor-worker/Cargo.toml @@ -9,7 +9,7 @@ clap = { workspace = true, features = ["derive"] } env_logger = { workspace = true } executor-core = { path = "../executor-core" } hex = "0.4.3" -intention-executor = { path = "../ethereum/intention-executor" } +intent-executor = { path = "../ethereum/intent-executor" } log = { workspace = true } parentchain-listener = { path = "../parentchain/listener" } scale-encode = { workspace = true } diff --git a/tee-worker/omni-executor/executor-worker/src/main.rs b/tee-worker/omni-executor/executor-worker/src/main.rs index 5dfd1137d7..7eb630e1ff 100644 --- a/tee-worker/omni-executor/executor-worker/src/main.rs +++ b/tee-worker/omni-executor/executor-worker/src/main.rs @@ -16,9 +16,8 @@ use crate::cli::Cli; use clap::Parser; -use intention_executor::EthereumIntentionExecutor; +use intent_executor::EthereumIntentExecutor; use log::error; -use parentchain_listener::CustomConfig; use std::io::Write; use std::thread::JoinHandle; use std::{fs, thread}; @@ -62,18 +61,17 @@ async fn listen_to_parentchain( ethereum_url: String, ) -> Result, ()> { let (_sub_stop_sender, sub_stop_receiver) = oneshot::channel(); - let ethereum_intention_executor = - EthereumIntentionExecutor::new(ðereum_url).map_err(|e| log::error!("{:?}", e))?; + let ethereum_intent_executor = + EthereumIntentExecutor::new(ðereum_url).map_err(|e| log::error!("{:?}", e))?; - let mut parentchain_listener = - parentchain_listener::create_listener::( - "litentry_rococo", - Handle::current(), - &parentchain_url, - ethereum_intention_executor, - sub_stop_receiver, - ) - .await?; + let mut parentchain_listener = parentchain_listener::create_listener::( + "litentry_rococo", + Handle::current(), + &parentchain_url, + ethereum_intent_executor, + sub_stop_receiver, + ) + .await?; Ok(thread::Builder::new() .name("litentry_rococo_sync".to_string()) diff --git a/tee-worker/omni-executor/parentchain/artifacts/rococo-omni-account.scale b/tee-worker/omni-executor/parentchain/artifacts/rococo-omni-account.scale index cc85f5c8e5..2c9a40ede9 100644 Binary files a/tee-worker/omni-executor/parentchain/artifacts/rococo-omni-account.scale and b/tee-worker/omni-executor/parentchain/artifacts/rococo-omni-account.scale differ diff --git a/tee-worker/omni-executor/parentchain/listener/Cargo.toml b/tee-worker/omni-executor/parentchain/listener/Cargo.toml index a33c0578a7..ce990f2bb4 100644 --- a/tee-worker/omni-executor/parentchain/listener/Cargo.toml +++ b/tee-worker/omni-executor/parentchain/listener/Cargo.toml @@ -7,10 +7,13 @@ edition.workspace = true [dependencies] async-trait = { workspace = true } executor-core = { path = "../../executor-core" } +hex = "0.4.3" log = { workspace = true } parity-scale-codec = { workspace = true, features = ["derive"] } scale-encode = "0.7.1" subxt = "0.37.0" +subxt-core = "0.37.0" +subxt-signer = { version = "0.37.0", features = ["subxt"] } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } [dev-dependencies] diff --git a/tee-worker/omni-executor/parentchain/listener/src/event_handler.rs b/tee-worker/omni-executor/parentchain/listener/src/event_handler.rs index 937452564c..e1f443752b 100644 --- a/tee-worker/omni-executor/parentchain/listener/src/event_handler.rs +++ b/tee-worker/omni-executor/parentchain/listener/src/event_handler.rs @@ -16,46 +16,109 @@ use crate::metadata::{MetadataProvider, SubxtMetadataProvider}; use crate::primitives::BlockEvent; +use crate::rpc_client::{SubstrateRpcClient, SubstrateRpcClientFactory}; use async_trait::async_trait; +use executor_core::event_handler::Error::RecoverableError; use executor_core::event_handler::{Error, EventHandler}; -use executor_core::intention_executor::IntentionExecutor; -use executor_core::primitives::Intention; +use executor_core::intent_executor::IntentExecutor; +use executor_core::key_store::KeyStore; +use executor_core::primitives::Intent; +use log::error; +use parity_scale_codec::Decode; use std::marker::PhantomData; +use std::sync::RwLock; +use subxt::config::DefaultExtrinsicParamsBuilder; use subxt::ext::scale_decode; use subxt::ext::scale_decode::DecodeAsFields; +use subxt::ext::subxt_core::tx; use subxt::{Config, Metadata}; +use subxt_core::config::DefaultExtrinsicParams; +use subxt_core::utils::{AccountId32, MultiAddress, MultiSignature}; +use subxt_signer::sr25519::SecretKeyBytes; -pub struct IntentionEventHandler< +pub struct IntentEventHandler< MetadataT, MetadataProviderT: MetadataProvider, - EthereumIntentionExecutorT: IntentionExecutor, + EthereumIntentExecutorT: IntentExecutor, + KeyStoreT: KeyStore, + RpcClient: SubstrateRpcClient, + RpcClientFactory: SubstrateRpcClientFactory, > { metadata_provider: MetadataProviderT, - ethereum_intention_executor: EthereumIntentionExecutorT, - phantom_data: PhantomData, + ethereum_intent_executor: EthereumIntentExecutorT, + key_store: KeyStoreT, + rpc_client_factory: RpcClientFactory, + nonce: RwLock, + phantom_data: PhantomData<(MetadataT, RpcClient)>, } impl< MetadataT, MetadataProviderT: MetadataProvider, - EthereumIntentionExecutorT: IntentionExecutor, - > IntentionEventHandler + EthereumIntentExecutorT: IntentExecutor, + KeyStoreT: KeyStore, + RpcClient: SubstrateRpcClient, + RpcClientFactory: SubstrateRpcClientFactory, + > + IntentEventHandler< + MetadataT, + MetadataProviderT, + EthereumIntentExecutorT, + KeyStoreT, + RpcClient, + RpcClientFactory, + > { pub fn new( metadata_provider: MetadataProviderT, - ethereum_intention_executor: EthereumIntentionExecutorT, + ethereum_intent_executor: EthereumIntentExecutorT, + key_store: KeyStoreT, + rpc_client_factory: RpcClientFactory, ) -> Self { - Self { metadata_provider, ethereum_intention_executor, phantom_data: Default::default() } + Self { + metadata_provider, + ethereum_intent_executor, + key_store, + rpc_client_factory, + nonce: RwLock::new(0), + phantom_data: Default::default(), + } } } #[async_trait] -impl - EventHandler - for IntentionEventHandler, EthereumIntentionExecutorT> +impl< + ChainConfig: Config< + ExtrinsicParams = DefaultExtrinsicParams, + AccountId = AccountId32, + Address = MultiAddress, + Signature = MultiSignature, + >, + EthereumIntentExecutorT: IntentExecutor + Send + Sync, + KeyStoreT: KeyStore + Send + Sync, + RpcClient: SubstrateRpcClient + Send + Sync, + RpcClientFactory: SubstrateRpcClientFactory + Send + Sync, + > EventHandler + for IntentEventHandler< + Metadata, + SubxtMetadataProvider, + EthereumIntentExecutorT, + KeyStoreT, + RpcClient, + RpcClientFactory, + > { async fn handle(&self, event: BlockEvent) -> Result<(), Error> { - log::debug!("Handling block event: {:?}", event.id); + log::debug!("Got event: {:?}, variant name: {}", event.id, event.variant_name); + + if event.pallet_name != "OmniAccount" || event.variant_name != "IntentRequested" { + // we are not interested in this event + log::debug!("Not interested in this event"); + return Ok(()); + } + + log::debug!("Got IntentRequested event: {:?}", event.id); + let metadata = self.metadata_provider.get(event.id.block_num).await; let pallet = metadata.pallet_by_name(&event.pallet_name).ok_or_else(move || { @@ -81,42 +144,116 @@ impl { - Intention::CallEthereum(call_ethereum.address.to_fixed_bytes(), call_ethereum.input.0) + let intent = match decoded.intent { + crate::litentry_rococo::runtime_types::core_primitives::intent::Intent::CallEthereum(call_ethereum) => { + Intent::CallEthereum(call_ethereum.address.to_fixed_bytes(), call_ethereum.input.0) }, - crate::litentry_rococo::runtime_types::core_primitives::intention::Intention::TransferEthereum(transfer) => { - Intention::TransferEthereum(transfer.to.to_fixed_bytes(), transfer.value) + crate::litentry_rococo::runtime_types::core_primitives::intent::Intent::TransferEthereum(transfer) => { + Intent::TransferEthereum(transfer.to.to_fixed_bytes(), transfer.value) } }; - //to explicitly handle all intention variants - match intention { - Intention::CallEthereum(_, _) => { - self.ethereum_intention_executor.execute(intention).await.map_err(|_| { + //to explicitly handle all intent variants + match intent { + Intent::CallEthereum(_, _) => { + self.ethereum_intent_executor.execute(intent).await.map_err(|_| { // assume for now we can easily recover - log::error!("Error executing intention"); + log::error!("Error executing intent"); Error::RecoverableError })?; }, - Intention::TransferEthereum(_, _) => { - self.ethereum_intention_executor.execute(intention).await.map_err(|_| { + Intent::TransferEthereum(_, _) => { + self.ethereum_intent_executor.execute(intent).await.map_err(|_| { // assume for now we can easily recover - log::error!("Error executing intention"); + log::error!("Error executing intent"); Error::RecoverableError })?; }, } + + log::debug!("Intent executed, publishing result"); + + // todo: the whole signing part should be encapsulated in separate component like `TransactionSigner` + //we need to report back to parachain intent result + let decoded = + crate::litentry_rococo::omni_account::events::IntentRequested::decode_as_fields( + &mut event.field_bytes.as_slice(), + &mut fields, + metadata.types(), + ) + .map_err(|_| { + log::error!("Could not decode event {:?}", event.id); + Error::NonRecoverableError + })?; + + let execution_result = + crate::litentry_rococo::omni_account::calls::types::intent_executed::Result::Success; + + let call = crate::litentry_rococo::tx().omni_account().intent_executed( + decoded.who, + decoded.intent, + execution_result, + ); + + let secret_key_bytes = self + .key_store + .read() + .map_err(|e| { + error!("Could not unseal key: {:?}", e); + }) + .unwrap(); + let signer = subxt_signer::sr25519::Keypair::from_secret_key(secret_key_bytes) + .map_err(|e| { + error!("Could not create secret key: {:?}", e); + }) + .unwrap(); + + let mut client = self.rpc_client_factory.new_client().await.map_err(|e| { + error!("Could not create RPC client: {:?}", e); + RecoverableError + })?; + let runtime_version = client.runtime_version().await.map_err(|e| { + error!("Could not get runtime version: {:?}", e); + RecoverableError + })?; + let genesis_hash = client.get_genesis_hash().await.map_err(|e| { + error!("Could not get genesis hash: {:?}", e); + RecoverableError + })?; + let nonce = *self.nonce.read().map_err(|e| { + error!("Could not read nonce: {:?}", e); + RecoverableError + })?; + let params = DefaultExtrinsicParamsBuilder::::new().nonce(nonce).build(); + *self.nonce.write().map_err(|e| { + error!("Could not write nonce: {:?}", e); + RecoverableError + })? = nonce + 1; + + let state = tx::ClientState:: { + metadata: { metadata }, + genesis_hash: ChainConfig::Hash::decode(&mut genesis_hash.as_slice()).unwrap(), + runtime_version: tx::RuntimeVersion { + spec_version: runtime_version.spec_version, + transaction_version: runtime_version.transaction_version, + }, + }; + let signed_call = tx::create_signed(&call, &state, &signer, params).unwrap(); + client.submit_tx(signed_call.encoded()).await.map_err(|e| { + error!("Error while submitting tx: {:?}", e); + RecoverableError + })?; + log::debug!("Result published"); Ok(()) } } diff --git a/tee-worker/omni-executor/parentchain/listener/src/fetcher.rs b/tee-worker/omni-executor/parentchain/listener/src/fetcher.rs index c8f9264d5a..7d3dfa800f 100644 --- a/tee-worker/omni-executor/parentchain/listener/src/fetcher.rs +++ b/tee-worker/omni-executor/parentchain/listener/src/fetcher.rs @@ -76,9 +76,6 @@ impl< if let Some(ref mut client) = self.client { client.get_block_events(block_num).await - // client.get_block_events(block_num).await.map(|events| { - // events.into_iter().map(|event| IntentionEvent::new(event.id)).collect() - // }) } else { Err(()) } diff --git a/tee-worker/omni-executor/parentchain/listener/src/key_store.rs b/tee-worker/omni-executor/parentchain/listener/src/key_store.rs new file mode 100644 index 0000000000..6c6df97527 --- /dev/null +++ b/tee-worker/omni-executor/parentchain/listener/src/key_store.rs @@ -0,0 +1,54 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use executor_core::key_store::KeyStore; +use subxt_signer::sr25519::SecretKeyBytes; + +/// Generates and stores keys used by for communication with parentchain` +pub struct SubstrateKeyStore { + path: String, +} + +impl SubstrateKeyStore { + pub fn new(path: String) -> Self { + let key = Self::generate_key().unwrap(); + let store = Self { path }; + store.write(&key).unwrap(); + + store + } +} + +impl KeyStore for SubstrateKeyStore { + fn generate_key() -> Result { + Ok([ + 45, 219, 105, 155, 49, 74, 164, 131, 153, 192, 15, 213, 225, 179, 167, 129, 12, 160, + 229, 37, 133, 168, 141, 233, 98, 117, 254, 112, 139, 210, 76, 6, + ]) + } + + fn serialize(k: &SecretKeyBytes) -> Result, ()> { + Ok(Vec::from(k)) + } + + fn deserialize(sealed: Vec) -> Result { + sealed.as_slice().try_into().map_err(|_| ()) + } + + fn path(&self) -> String { + self.path.clone() + } +} diff --git a/tee-worker/omni-executor/parentchain/listener/src/lib.rs b/tee-worker/omni-executor/parentchain/listener/src/lib.rs index 7ef8f940d2..9f8fab0384 100644 --- a/tee-worker/omni-executor/parentchain/listener/src/lib.rs +++ b/tee-worker/omni-executor/parentchain/listener/src/lib.rs @@ -16,22 +16,27 @@ mod event_handler; mod fetcher; +mod key_store; mod listener; mod metadata; mod primitives; mod rpc_client; -use crate::event_handler::IntentionEventHandler; +use crate::event_handler::IntentEventHandler; use crate::fetcher::Fetcher; +use crate::key_store::SubstrateKeyStore; use crate::listener::ParentchainListener; use crate::metadata::SubxtMetadataProvider; use crate::rpc_client::{SubxtClient, SubxtClientFactory}; -use executor_core::intention_executor::IntentionExecutor; +use executor_core::intent_executor::IntentExecutor; +use executor_core::key_store::KeyStore; use executor_core::listener::Listener; use executor_core::sync_checkpoint_repository::FileCheckpointRepository; +use log::{error, info}; use scale_encode::EncodeAsType; use subxt::config::signed_extensions; use subxt::Config; +use subxt_core::utils::AccountId32; use tokio::runtime::Handle; use tokio::sync::oneshot::Receiver; @@ -48,7 +53,7 @@ pub enum CustomConfig {} impl Config for CustomConfig { type Hash = subxt::utils::H256; type AccountId = subxt::utils::AccountId32; - type Address = subxt::utils::MultiAddress; + type Address = subxt::utils::MultiAddress; type Signature = subxt::utils::MultiSignature; type Hasher = subxt::config::substrate::BlakeTwo256; type Header = subxt::config::substrate::SubstrateHeader; @@ -71,40 +76,56 @@ impl Config for CustomConfig { } /// Creates parentchain listener -pub async fn create_listener< - ChainConfig: Config, - EthereumIntentionExecutorT: IntentionExecutor + Send + Sync, ->( +pub async fn create_listener( id: &str, handle: Handle, ws_rpc_endpoint: &str, - ethereum_intention_executor: EthereumIntentionExecutorT, + ethereum_intent_executor: EthereumIntentExecutorT, stop_signal: Receiver<()>, ) -> Result< ParentchainListener< - SubxtClient, - SubxtClientFactory, + SubxtClient, + SubxtClientFactory, FileCheckpointRepository, - ChainConfig, - EthereumIntentionExecutorT, + CustomConfig, + EthereumIntentExecutorT, >, (), > { - let client_factory: SubxtClientFactory = SubxtClientFactory::new(ws_rpc_endpoint); + let client_factory: SubxtClientFactory = SubxtClientFactory::new(ws_rpc_endpoint); let fetcher = Fetcher::new(client_factory); let last_processed_log_repository = FileCheckpointRepository::new("data/parentchain_last_log.bin"); let metadata_provider = SubxtMetadataProvider::new(SubxtClientFactory::new(ws_rpc_endpoint)); - let intention_event_handler = - IntentionEventHandler::new(metadata_provider, ethereum_intention_executor); + let key_store = SubstrateKeyStore::new("/data/parentchain_key.bin".to_string()); + let secret_key_bytes = key_store + .read() + .map_err(|e| { + error!("Could not unseal key: {:?}", e); + }) + .unwrap(); + let signer = subxt_signer::sr25519::Keypair::from_secret_key(secret_key_bytes) + .map_err(|e| { + error!("Could not create secret key: {:?}", e); + }) + .unwrap(); + + info!("Substrate signer address: {}", AccountId32::from(signer.public_key())); + + let intent_event_handler = IntentEventHandler::new( + metadata_provider, + ethereum_intent_executor, + key_store, + SubxtClientFactory::new(ws_rpc_endpoint), + ); Listener::new( id, handle, fetcher, - intention_event_handler, + intent_event_handler, stop_signal, last_processed_log_repository, ) diff --git a/tee-worker/omni-executor/parentchain/listener/src/listener.rs b/tee-worker/omni-executor/parentchain/listener/src/listener.rs index b32d65a93d..dabd440fa0 100644 --- a/tee-worker/omni-executor/parentchain/listener/src/listener.rs +++ b/tee-worker/omni-executor/parentchain/listener/src/listener.rs @@ -14,27 +14,35 @@ // You should have received a copy of the GNU General Public License // along with Litentry. If not, see . -use crate::event_handler::IntentionEventHandler; +use crate::event_handler::IntentEventHandler; use crate::fetcher::Fetcher; +use crate::key_store::SubstrateKeyStore; use crate::metadata::SubxtMetadataProvider; use crate::primitives::SyncCheckpoint; use crate::primitives::{BlockEvent, EventId}; use executor_core::listener::Listener; use subxt::Metadata; -pub type IntentionEventId = EventId; +pub type IntentEventId = EventId; pub type ParentchainListener< RpcClient, RpcClientFactory, CheckpointRepository, ChainConfig, - EthereumIntentionExecutor, + EthereumIntentExecutor, > = Listener< Fetcher, SyncCheckpoint, CheckpointRepository, - IntentionEventId, + IntentEventId, BlockEvent, - IntentionEventHandler, EthereumIntentionExecutor>, + IntentEventHandler< + Metadata, + SubxtMetadataProvider, + EthereumIntentExecutor, + SubstrateKeyStore, + RpcClient, + RpcClientFactory, + >, >; diff --git a/tee-worker/omni-executor/parentchain/listener/src/metadata.rs b/tee-worker/omni-executor/parentchain/listener/src/metadata.rs index fbd0c9838f..c6e930ab14 100644 --- a/tee-worker/omni-executor/parentchain/listener/src/metadata.rs +++ b/tee-worker/omni-executor/parentchain/listener/src/metadata.rs @@ -38,7 +38,7 @@ impl SubxtMetadataProvider { impl MetadataProvider for SubxtMetadataProvider { async fn get(&self, block_num: u64) -> Metadata { let mut client = self.client_factory.new_client().await.unwrap(); - let raw_metadata = client.get_raw_metadata(block_num).await.unwrap(); + let raw_metadata = client.get_raw_metadata(Some(block_num)).await.unwrap(); Metadata::decode(&mut raw_metadata.as_slice()).unwrap() } diff --git a/tee-worker/omni-executor/parentchain/listener/src/primitives.rs b/tee-worker/omni-executor/parentchain/listener/src/primitives.rs index 08e161e198..1dd0ef58a4 100644 --- a/tee-worker/omni-executor/parentchain/listener/src/primitives.rs +++ b/tee-worker/omni-executor/parentchain/listener/src/primitives.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Litentry. If not, see . -use crate::listener::IntentionEventId; +use crate::listener::IntentEventId; use executor_core::primitives::GetEventId; use executor_core::sync_checkpoint_repository::Checkpoint; use parity_scale_codec::{Decode, Encode}; -/// Used to uniquely identify intention event on parentchain. +/// Used to uniquely identify intent event on parentchain. #[derive(Clone, Debug)] pub struct EventId { pub block_num: u64, @@ -73,8 +73,8 @@ impl From for SyncCheckpoint { } } -impl From for SyncCheckpoint { - fn from(event_id: IntentionEventId) -> Self { +impl From for SyncCheckpoint { + fn from(event_id: IntentEventId) -> Self { Self::from_event_id(&event_id) } } diff --git a/tee-worker/omni-executor/parentchain/listener/src/rpc_client.rs b/tee-worker/omni-executor/parentchain/listener/src/rpc_client.rs index a42f6b34f3..672aa0a53a 100644 --- a/tee-worker/omni-executor/parentchain/listener/src/rpc_client.rs +++ b/tee-worker/omni-executor/parentchain/listener/src/rpc_client.rs @@ -16,6 +16,7 @@ use crate::primitives::{BlockEvent, EventId}; use async_trait::async_trait; +use log::error; use parity_scale_codec::Encode; use std::marker::PhantomData; use std::ops::Deref; @@ -25,12 +26,20 @@ use subxt::config::Header; use subxt::events::EventsClient; use subxt::{Config, OnlineClient}; +pub struct RuntimeVersion { + pub spec_version: u32, + pub transaction_version: u32, +} + /// For fetching data from Substrate RPC node #[async_trait] pub trait SubstrateRpcClient { async fn get_last_finalized_block_num(&mut self) -> Result; async fn get_block_events(&mut self, block_num: u64) -> Result, ()>; - async fn get_raw_metadata(&mut self, block_num: u64) -> Result, ()>; + async fn get_raw_metadata(&mut self, block_num: Option) -> Result, ()>; + async fn submit_tx(&mut self, raw_tx: &[u8]) -> Result<(), ()>; + async fn runtime_version(&mut self) -> Result; + async fn get_genesis_hash(&mut self) -> Result, ()>; } pub struct SubxtClient { @@ -72,11 +81,35 @@ impl SubstrateRpcClient for SubxtClient { } } - async fn get_raw_metadata(&mut self, block_num: u64) -> Result, ()> { - let maybe_hash = - self.legacy.chain_get_block_hash(Some(block_num.into())).await.map_err(|_| ())?; + async fn get_raw_metadata(&mut self, block_num: Option) -> Result, ()> { + let maybe_hash = self + .legacy + .chain_get_block_hash(block_num.map(|b| b.into())) + .await + .map_err(|_| ())?; Ok(self.legacy.state_get_metadata(maybe_hash).await.unwrap().deref().encode()) } + + async fn submit_tx(&mut self, raw_tx: &[u8]) -> Result<(), ()> { + self.legacy.author_submit_extrinsic(raw_tx).await.map(|_| ()).map_err(|e| { + error!("Could not submit tx: {:?}", e); + }) + } + + async fn runtime_version(&mut self) -> Result { + self.legacy + .state_get_runtime_version(None) + .await + .map(|rv| RuntimeVersion { + spec_version: rv.spec_version, + transaction_version: rv.transaction_version, + }) + .map_err(|_| ()) + } + + async fn get_genesis_hash(&mut self) -> Result, ()> { + self.legacy.genesis_hash().await.map(|h| h.encode()).map_err(|_| ()) + } } pub struct MockedRpcClient { @@ -93,7 +126,19 @@ impl SubstrateRpcClient for MockedRpcClient { Ok(vec![]) } - async fn get_raw_metadata(&mut self, _block_num: u64) -> Result, ()> { + async fn get_raw_metadata(&mut self, _block_num: Option) -> Result, ()> { + Ok(vec![]) + } + + async fn submit_tx(&mut self, _raw_tx: &[u8]) -> Result<(), ()> { + Ok(()) + } + + async fn runtime_version(&mut self) -> Result { + Ok(RuntimeVersion { spec_version: 0, transaction_version: 0 }) + } + + async fn get_genesis_hash(&mut self) -> Result, ()> { Ok(vec![]) } }