From d61f57fd72616ecc5006018ba8f696d7b06f6cdb Mon Sep 17 00:00:00 2001 From: Marko Petrlic Date: Fri, 20 Mar 2026 22:45:21 +0100 Subject: [PATCH 1/2] Expanded extrinsics rpc --- node/src/rpc.rs | 4 +- rpc/kate-rpc/src/{system.rs => custom.rs} | 443 +++++++++++++--------- rpc/kate-rpc/src/lib.rs | 2 +- 3 files changed, 260 insertions(+), 189 deletions(-) rename rpc/kate-rpc/src/{system.rs => custom.rs} (55%) diff --git a/node/src/rpc.rs b/node/src/rpc.rs index 59cd8d733..b458b83a5 100644 --- a/node/src/rpc.rs +++ b/node/src/rpc.rs @@ -249,8 +249,8 @@ where GrandpaJustifications::::new(client.clone()), ))?; - io.merge(kate_rpc::system::ApiServer::into_rpc( - kate_rpc::system::Rpc::::new(client), + io.merge(kate_rpc::custom::ApiServer::into_rpc( + kate_rpc::custom::Rpc::::new(client), ))?; Ok(io) diff --git a/rpc/kate-rpc/src/system.rs b/rpc/kate-rpc/src/custom.rs similarity index 55% rename from rpc/kate-rpc/src/system.rs rename to rpc/kate-rpc/src/custom.rs index dc32e2fc3..62521680b 100644 --- a/rpc/kate-rpc/src/system.rs +++ b/rpc/kate-rpc/src/custom.rs @@ -1,7 +1,7 @@ use codec::{Compact, Decode, Encode}; -use da_runtime::Preamble; +use da_runtime::{AccountId, Preamble}; use fetch_events::AllowedEvents; -use fetch_extrinsics::{AllowedExtrinsic, DataFormat, Extrinsic, Extrinsics, SignatureFilter}; +use fetch_extrinsics::{AllowedExtrinsic, DataFormat, Extrinsic, Extrinsics, Query}; use frame_system_rpc_runtime_api::SystemEventsApi; use jsonrpsee::{ core::{async_trait, RpcResult}, @@ -34,7 +34,7 @@ pub trait Api { &self, at: types::BlockId, allow_list: Option>, - sig_filter: SignatureFilter, + query: Query, data_format: DataFormat, ) -> RpcResult; @@ -89,6 +89,10 @@ impl Error { pub fn into_error_object<'a>(self, msg: String) -> ErrorObject<'a> { ErrorObject::owned(i32::from(self), msg, None::<()>) } + + pub fn other_error<'a>(msg: String) -> ErrorObject<'a> { + ErrorObject::owned(i32::from(Self::Other), msg, None::<()>) + } } impl From for i32 { @@ -116,26 +120,14 @@ where { async fn events( &self, - block_id: types::BlockId, + at: types::BlockId, filter: fetch_events::AllowedEvents, fetch_data: bool, ) -> RpcResult { use fetch_events::PhaseEvents; - let block_hash = match block_id { - types::BlockId::Hash(hash) => hash, - types::BlockId::Number(number) => { - let hash = match self.client.block_hash(number.into()) { - Ok(ok) => ok, - Err(err) => return Err(Error::NoBlockFound.into_error_object(err.to_string())), - }; - let Some(hash) = hash else { - return Err(Error::NoBlockFound - .into_error_object(String::from("Failed to find block hash"))); - }; - hash.into() - }, - }; + let block_hash = block_hash_from_block_id(&self.client, at) + .map_err(|e| Error::NoBlockFound.into_error_object(e))?; let runtime_api = self.client.runtime_api(); let result = runtime_api @@ -149,22 +141,13 @@ where } } - #[tracing::instrument( - name = "http.request", - skip_all, - fields( - http.method = "POST", - http.route = "/extrinsics" - ) - )] async fn extrinsics( &self, at: types::BlockId, allow_list: Option>, - sig_filter: SignatureFilter, + query: Query, data_format: DataFormat, ) -> RpcResult { - use types::BlockId; const MAX_INDICES_COUNT: usize = 30; if let Some(allow_list) = &allow_list { @@ -175,57 +158,54 @@ where } } - let block_hash = match at { - BlockId::Hash(h) => h, - BlockId::Number(n) => { - let hash = match self.client.block_hash(n.into()) { - Ok(ok) => ok, - Err(err) => return Err(Error::NoBlockFound.into_error_object(err.to_string())), - }; - let Some(hash) = hash else { - return Err(Error::NoBlockFound - .into_error_object(String::from("Failed to find block hash"))); - }; - hash.into() - }, - }; + let block_hash = block_hash_from_block_id(&self.client, at) + .map_err(|e| Error::NoBlockFound.into_error_object(e))?; - let block_body = match self.client.block_body(block_hash.into()) { - Ok(x) => x, - Err(e) => { - return Err(Error::Other.into_error_object(e.to_string())); - }, - }; + let block_body = self + .client + .block_body(block_hash.into()) + .map_err(|e| Error::other_error(e.to_string()))?; let Some(block_body) = block_body else { return Ok(Vec::new()); }; - let (allowed_indices, allowed_hashes, allowed_pallets, allowed_calls) = - allowed_extrinsics_to_parts(allow_list); + let limit = query.max_items.unwrap_or(u32::MAX) as usize; + if limit == 0 { + return Ok(Vec::new()); + } + + let mut extrinsics_to_return = Vec::new(); + let body_iter: Box> = if query.reverse { + Box::new(block_body.into_iter().enumerate().rev()) + } else { + Box::new(block_body.into_iter().enumerate()) + }; + + let allow_list = AllowedExtrinsicsWrapper::new(allow_list); + for (ext_index, opaque) in body_iter { + let mut wrapper = ExtrinsicWrapper::new(opaque, ext_index as u32); - let mut returned_extrinsics = Vec::new(); - for (ext_index, opaque) in block_body.into_iter().enumerate() { - let ext_index = ext_index as u32; - // Filter Indices - if let Some(allowed) = &allowed_indices { - if !allowed.contains(&ext_index) { - continue; - } + if !allow_list.index_allowed(wrapper.ext_index) { + continue; } - let transparent = TransparentOpaque::from_opaque(&opaque)?; + let ext_hash = wrapper.hash().map_err(|e| Error::other_error(e))?; + if !allow_list.hash_allowed(ext_hash) { + continue; + } - let mut account_id = None; - let mut nonce = None; - if let Some((address, _, extended)) = transparent.preamble.to_signed() { - nonce = Some(extended.5 .0); - if let MultiAddress::Id(id) = address { - account_id = Some(id); - } + let ext_call_id = wrapper.call_id().map_err(|e| Error::other_error(e))?; + if !allow_list.pallet_allowed(ext_call_id.0) { + continue; + } + if !allow_list.call_allowed(&ext_call_id) { + continue; } - if let Some(allowed_address) = &sig_filter.account_id { + let account_id = wrapper.account_id().map_err(|e| Error::other_error(e))?; + let nonce = wrapper.nonce().map_err(|e| Error::other_error(e))?; + if let Some(allowed_address) = &query.address { let Some(account) = account_id.as_ref() else { continue; }; @@ -235,60 +215,38 @@ where } } - if let Some(allowed_nonce) = &sig_filter.nonce { - let Some(nonce) = nonce.as_ref() else { - continue; - }; - if *allowed_nonce != *nonce { - continue; - } - } - - // Filter Pallets - if let Some(allowed) = &allowed_pallets { - if !allowed.contains(&transparent.pallet_id) { - continue; - } - } - - // Filter Calls - if let Some(allowed) = &allowed_calls { - if !allowed.contains(&(transparent.pallet_id, transparent.variant_id)) { - continue; - } - } - - let ext_hash = Blake2Hasher::hash(&transparent.bytes); - - // Filter Hashes - if let Some(allowed) = &allowed_hashes { - if !allowed.contains(&ext_hash) { + if let Some(allowed_nonce) = query.nonce { + if Some(allowed_nonce) != nonce { continue; } } + let transparent = wrapper.transparent().map_err(|e| Error::other_error(e))?; let data = match data_format { DataFormat::None => String::new(), DataFormat::Call => { const_hex::encode(&transparent.bytes[transparent.call_start_pos..]) }, - DataFormat::Extrinsic => const_hex::encode(transparent.bytes), + DataFormat::Extrinsic => const_hex::encode(&transparent.bytes), }; let ext = Extrinsic { data, ext_hash, - ext_index, + ext_index: ext_index as u32, pallet_id: transparent.pallet_id, variant_id: transparent.variant_id, account_id, nonce, }; - returned_extrinsics.push(ext); + extrinsics_to_return.push(ext); + if extrinsics_to_return.len() >= limit { + break; + } } - Ok(returned_extrinsics) + Ok(extrinsics_to_return) } async fn chain_info(&self) -> RpcResult { @@ -314,27 +272,13 @@ where const TIMESTAMP_SET_PALLET_ID: u8 = 3; const TIMESTAMP_SET_VARIANT_ID: u8 = 0; - let block_hash = match at { - types::BlockId::Hash(h) => h, - types::BlockId::Number(n) => { - let hash = match self.client.block_hash(n.into()) { - Ok(ok) => ok, - Err(err) => return Err(Error::NoBlockFound.into_error_object(err.to_string())), - }; - let Some(hash) = hash else { - return Err(Error::NoBlockFound - .into_error_object(String::from("Failed to find block hash"))); - }; - hash.into() - }, - }; + let block_hash = block_hash_from_block_id(&self.client, at) + .map_err(|e| Error::NoBlockFound.into_error_object(e))?; - let block_body = match self.client.block_body(block_hash.into()) { - Ok(x) => x, - Err(e) => { - return Err(Error::Other.into_error_object(e.to_string())); - }, - }; + let block_body = self + .client + .block_body(block_hash.into()) + .map_err(|e| Error::other_error(e.to_string()))?; let Some(block_body) = block_body else { return Ok(0); @@ -344,7 +288,8 @@ where return Ok(0); }; - let transparent = TransparentOpaque::from_opaque(opaque)?; + let transparent = + TransparentOpaque::from_opaque(opaque).map_err(|e| Error::other_error(e))?; if (transparent.pallet_id != TIMESTAMP_SET_PALLET_ID) || (transparent.variant_id != TIMESTAMP_SET_VARIANT_ID) @@ -362,6 +307,185 @@ where } } +fn block_hash_from_block_id(client: &Arc, at: types::BlockId) -> Result +where + C: ProvideRuntimeApi + Send + Sync + 'static, + C: BlockBackend, + C::Api: frame_system_rpc_runtime_api::SystemEventsApi, + Block: BlockT, + ::Hash: From + Into, +{ + let block_hash = match at { + types::BlockId::Hash(h) => h, + types::BlockId::Number(n) => { + let hash = match client.block_hash(n.into()) { + Ok(ok) => ok, + Err(err) => return Err(err.to_string()), + }; + let Some(hash) = hash else { + return Err(String::from("Failed to find block hash")); + }; + hash.into() + }, + }; + + Ok(block_hash) +} + +#[derive(Default)] +struct AllowedExtrinsicsWrapper { + allowed_indices: Option>, + allowed_hashes: Option>, + allowed_pallets: Option>, + allowed_calls: Option>, +} + +impl AllowedExtrinsicsWrapper { + pub fn new(list: Option>) -> Self { + let Some(list) = list else { + return Self::default(); + }; + + let mut allowed_indices: Option> = None; + let mut allowed_hashes: Option> = None; + let mut allowed_pallets: Option> = None; + let mut allowed_calls: Option> = None; + + for allowed in list { + match allowed { + AllowedExtrinsic::TxHash(x) => { + if let Some(hashes) = allowed_hashes.as_mut() { + hashes.push(x); + } else { + allowed_hashes = Some(vec![x]) + } + }, + AllowedExtrinsic::TxIndex(x) => { + if let Some(items) = allowed_indices.as_mut() { + items.push(x); + } else { + allowed_indices = Some(vec![x]) + } + }, + AllowedExtrinsic::Pallet(x) => { + if let Some(items) = allowed_pallets.as_mut() { + items.push(x); + } else { + allowed_pallets = Some(vec![x]) + } + }, + AllowedExtrinsic::PalletCall(x) => { + if let Some(items) = allowed_calls.as_mut() { + items.push(x); + } else { + allowed_calls = Some(vec![x]) + } + }, + } + } + + Self { + allowed_indices, + allowed_hashes, + allowed_pallets, + allowed_calls, + } + } + + pub fn index_allowed(&self, index: u32) -> bool { + let Some(list) = self.allowed_indices.as_ref() else { + return true; + }; + return list.contains(&index); + } + + pub fn hash_allowed(&self, hash: H256) -> bool { + let Some(list) = self.allowed_hashes.as_ref() else { + return true; + }; + return list.contains(&hash); + } + + pub fn pallet_allowed(&self, pallet: u8) -> bool { + let Some(list) = self.allowed_pallets.as_ref() else { + return true; + }; + return list.contains(&pallet); + } + + pub fn call_allowed(&self, call: &(u8, u8)) -> bool { + let Some(list) = self.allowed_calls.as_ref() else { + return true; + }; + return list.contains(call); + } +} + +struct ExtrinsicWrapper { + pub opaque: OpaqueExtrinsic, + pub transparent: Option, + pub ext_index: u32, +} + +impl ExtrinsicWrapper { + pub fn new(opaque: OpaqueExtrinsic, ext_index: u32) -> Self { + Self { + opaque, + transparent: None, + ext_index, + } + } + + pub fn hash(&mut self) -> Result { + let transparent = self.transparent()?; + let hash = Blake2Hasher::hash(&transparent.bytes); + return Ok(hash); + } + + pub fn call_id(&mut self) -> Result<(u8, u8), String> { + let transparent = self.transparent()?; + return Ok((transparent.pallet_id, transparent.variant_id)); + } + + pub fn nonce(&mut self) -> Result, String> { + let transparent = self.transparent()?; + if let Preamble::Signed(_, _, extended) = &transparent.preamble { + return Ok(Some(extended.5 .0)); + } + + Ok(None) + } + + pub fn account_id(&mut self) -> Result, String> { + let transparent = self.transparent()?; + if let Preamble::Signed(address, _, _) = &transparent.preamble { + if let MultiAddress::Id(id) = address { + return Ok(Some(id.clone())); + } + + if let MultiAddress::Address32(id) = address { + return Ok(Some(AccountId::from(id.clone()))); + } + } + + Ok(None) + } + + fn transparent(&mut self) -> Result<&TransparentOpaque, String> { + if self.transparent.is_none() { + let transparent = TransparentOpaque::from_opaque(&self.opaque)?; + self.transparent = Some(transparent); + } + + // Will never fail + self.transparent + .as_ref() + .ok_or(String::from("Failed to find transparent extrinsic.")) + } +} + +impl ExtrinsicWrapper {} + struct TransparentOpaque { pub bytes: Vec, pub call_start_pos: usize, @@ -371,29 +495,29 @@ struct TransparentOpaque { } impl TransparentOpaque { - pub fn from_opaque<'a>(opaque: &OpaqueExtrinsic) -> Result> { + pub fn from_opaque(opaque: &OpaqueExtrinsic) -> Result { let bytes = opaque.encode(); let mut iter = bytes.as_slice(); let _ = match Compact::::decode(&mut iter) { Ok(x) => x, Err(e) => { - return Err(Error::Other.into_error_object(e.to_string())); + return Err(e.to_string()); }, }; let preamble = match Preamble::decode(&mut iter) { Ok(p) => p, Err(e) => { - return Err(Error::Other.into_error_object(e.to_string())); + return Err(e.to_string()); }, }; let call_start_pos = bytes.len().saturating_sub(iter.len()); let pallet_id = *bytes .get(call_start_pos) - .ok_or(Error::Other.into_error_object(String::from("Invalid extrinsic found.")))?; + .ok_or(String::from("Invalid extrinsic found."))?; let variant_id = *bytes .get(call_start_pos + 1) - .ok_or(Error::Other.into_error_object(String::from("Invalid extrinsic found.")))?; + .ok_or(String::from("Invalid extrinsic found."))?; let res = TransparentOpaque { bytes, @@ -406,64 +530,6 @@ impl TransparentOpaque { } } -fn allowed_extrinsics_to_parts( - list: Option>, -) -> ( - Option>, - Option>, - Option>, - Option>, -) { - let Some(list) = list else { - return (None, None, None, None); - }; - - let mut allowed_indices: Option> = None; - let mut allowed_hashes: Option> = None; - let mut allowed_pallets: Option> = None; - let mut allowed_calls: Option> = None; - - for allowed in list { - match allowed { - AllowedExtrinsic::TxHash(x) => { - if let Some(hashes) = allowed_hashes.as_mut() { - hashes.push(x); - } else { - allowed_hashes = Some(vec![x]) - } - }, - AllowedExtrinsic::TxIndex(x) => { - if let Some(items) = allowed_indices.as_mut() { - items.push(x); - } else { - allowed_indices = Some(vec![x]) - } - }, - AllowedExtrinsic::Pallet(x) => { - if let Some(items) = allowed_pallets.as_mut() { - items.push(x); - } else { - allowed_pallets = Some(vec![x]) - } - }, - AllowedExtrinsic::PalletCall(x) => { - if let Some(items) = allowed_calls.as_mut() { - items.push(x); - } else { - allowed_calls = Some(vec![x]) - } - }, - } - } - - ( - allowed_indices, - allowed_hashes, - allowed_pallets, - allowed_calls, - ) -} - pub mod types { use super::*; @@ -589,9 +655,14 @@ pub mod fetch_extrinsics { } #[derive(Default, Clone, Serialize, Deserialize)] - pub struct SignatureFilter { - // SS58 address - pub account_id: Option, + pub struct Query { + /// SS58 address + pub address: Option, + /// Nonce pub nonce: Option, + /// If set to Some(1) it will return only the first occurrence of a extrinsic that match. + pub max_items: Option, + /// If true, it will traverse the extrinsic list from end to start. + pub reverse: bool, } } diff --git a/rpc/kate-rpc/src/lib.rs b/rpc/kate-rpc/src/lib.rs index d89e3fa2f..8374bdca6 100644 --- a/rpc/kate-rpc/src/lib.rs +++ b/rpc/kate-rpc/src/lib.rs @@ -27,9 +27,9 @@ pub type Rows = BoundedVec; pub type MaxCells = ConstU32<10_000>; pub type Cells = BoundedVec; +pub mod custom; pub mod justifications; pub mod metrics; -pub mod system; #[derive(Clone, Default)] pub struct Deps { From c58b7bdd1463686a2286453711f515a508b0b046 Mon Sep 17 00:00:00 2001 From: Marko Petrlic Date: Fri, 20 Mar 2026 23:21:32 +0100 Subject: [PATCH 2/2] Added cache --- Cargo.lock | 1 + rpc/kate-rpc/Cargo.toml | 1 + rpc/kate-rpc/src/custom.rs | 153 ++++++++++++++++++++++++++++++------- 3 files changed, 129 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cecfa9747..c8facfea0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6587,6 +6587,7 @@ dependencies = [ "kate", "kate-recovery", "log", + "lru 0.16.3", "parity-scale-codec", "parking_lot 0.12.5", "sc-client-api", diff --git a/rpc/kate-rpc/Cargo.toml b/rpc/kate-rpc/Cargo.toml index 289a2bd3f..2fb8e7cc6 100644 --- a/rpc/kate-rpc/Cargo.toml +++ b/rpc/kate-rpc/Cargo.toml @@ -18,6 +18,7 @@ serde_json.workspace = true serde.workspace = true const-hex.workspace = true tracing = { workspace = true } +lru = { workspace = true } # 3rd party jsonrpsee.workspace = true diff --git a/rpc/kate-rpc/src/custom.rs b/rpc/kate-rpc/src/custom.rs index 62521680b..0768e12c6 100644 --- a/rpc/kate-rpc/src/custom.rs +++ b/rpc/kate-rpc/src/custom.rs @@ -48,6 +48,8 @@ pub trait Api { async fn block_timestamp(&self, block_id: types::BlockId) -> RpcResult; } +type CachedExtrinsics = lru::LruCache<(H256, u32), CachedExtrinsic>; + pub struct Rpc where C: ProvideRuntimeApi + Send + Sync + 'static, @@ -55,6 +57,7 @@ where Block: BlockT, { pub client: Arc, + pub cached_extrinsics: Arc>, _phantom: PhantomData, } impl Rpc @@ -66,8 +69,11 @@ where ::Hash: From, { pub fn new(client: Arc) -> Self { + let cached_extrinsics = + lru::LruCache::new(unsafe { std::num::NonZeroUsize::new_unchecked(1000) }); Self { client, + cached_extrinsics: Arc::new(parking_lot::Mutex::new(cached_extrinsics)), _phantom: PhantomData, } } @@ -184,18 +190,22 @@ where let allow_list = AllowedExtrinsicsWrapper::new(allow_list); for (ext_index, opaque) in body_iter { - let mut wrapper = ExtrinsicWrapper::new(opaque, ext_index as u32); + let mut wrapper = ExtrinsicWrapper::new(opaque, block_hash, ext_index as u32); - if !allow_list.index_allowed(wrapper.ext_index) { + if !allow_list.index_allowed(ext_index as u32) { continue; } - let ext_hash = wrapper.hash().map_err(|e| Error::other_error(e))?; + let ext_hash = wrapper + .hash(&self.cached_extrinsics) + .map_err(|e| Error::other_error(e))?; if !allow_list.hash_allowed(ext_hash) { continue; } - let ext_call_id = wrapper.call_id().map_err(|e| Error::other_error(e))?; + let ext_call_id = wrapper + .call_id(&self.cached_extrinsics) + .map_err(|e| Error::other_error(e))?; if !allow_list.pallet_allowed(ext_call_id.0) { continue; } @@ -203,8 +213,12 @@ where continue; } - let account_id = wrapper.account_id().map_err(|e| Error::other_error(e))?; - let nonce = wrapper.nonce().map_err(|e| Error::other_error(e))?; + let account_id = wrapper + .account_id(&self.cached_extrinsics) + .map_err(|e| Error::other_error(e))?; + let nonce = wrapper + .nonce(&self.cached_extrinsics) + .map_err(|e| Error::other_error(e))?; if let Some(allowed_address) = &query.address { let Some(account) = account_id.as_ref() else { continue; @@ -332,6 +346,14 @@ where Ok(block_hash) } +#[derive(Default)] +pub struct CachedExtrinsic { + hash: Option, + call_id: Option<(u8, u8)>, + account_id: Option>, + nonce: Option>, +} + #[derive(Default)] struct AllowedExtrinsicsWrapper { allowed_indices: Option>, @@ -424,51 +446,132 @@ impl AllowedExtrinsicsWrapper { struct ExtrinsicWrapper { pub opaque: OpaqueExtrinsic, pub transparent: Option, - pub ext_index: u32, + pub key: (H256, u32), } impl ExtrinsicWrapper { - pub fn new(opaque: OpaqueExtrinsic, ext_index: u32) -> Self { + pub fn new(opaque: OpaqueExtrinsic, block_hash: H256, ext_index: u32) -> Self { Self { opaque, transparent: None, - ext_index, + key: (block_hash, ext_index), } } - pub fn hash(&mut self) -> Result { + pub fn hash( + &mut self, + cache: &Arc>, + ) -> Result { + // Check cache + { + let mut lock = cache.lock(); + let cached = lock.get(&self.key); + if let Some(hash) = cached.and_then(|x| x.hash) { + return Ok(hash); + } + } + let transparent = self.transparent()?; let hash = Blake2Hasher::hash(&transparent.bytes); + + // Set cache + { + let mut lock = cache.lock(); + let cached = lock.get_or_insert_mut(self.key, || Default::default()); + cached.hash = Some(hash); + } + return Ok(hash); } - pub fn call_id(&mut self) -> Result<(u8, u8), String> { + pub fn call_id( + &mut self, + cache: &Arc>, + ) -> Result<(u8, u8), String> { + // Check cache + { + let mut lock = cache.lock(); + let cached = lock.get(&self.key); + if let Some(call_id) = cached.and_then(|x| x.call_id) { + return Ok(call_id); + } + } + let transparent = self.transparent()?; - return Ok((transparent.pallet_id, transparent.variant_id)); + let call_id = (transparent.pallet_id, transparent.variant_id); + + // Set cache + { + let mut lock = cache.lock(); + let cached = lock.get_or_insert_mut(self.key, || Default::default()); + cached.call_id = Some(call_id); + } + + return Ok(call_id); } - pub fn nonce(&mut self) -> Result, String> { + pub fn nonce( + &mut self, + cache: &Arc>, + ) -> Result, String> { + // Check cache + { + let mut lock = cache.lock(); + let cached = lock.get(&self.key); + if let Some(nonce) = cached.and_then(|x| x.nonce) { + return Ok(nonce); + } + } + let transparent = self.transparent()?; - if let Preamble::Signed(_, _, extended) = &transparent.preamble { - return Ok(Some(extended.5 .0)); + let nonce = if let Preamble::Signed(_, _, extended) = &transparent.preamble { + Some(extended.5 .0) + } else { + None + }; + + // Set cache + { + let mut lock = cache.lock(); + let cached = lock.get_or_insert_mut(self.key, || Default::default()); + cached.nonce = Some(nonce); } - Ok(None) + return Ok(nonce); } - pub fn account_id(&mut self) -> Result, String> { - let transparent = self.transparent()?; - if let Preamble::Signed(address, _, _) = &transparent.preamble { - if let MultiAddress::Id(id) = address { - return Ok(Some(id.clone())); + pub fn account_id( + &mut self, + cache: &Arc>, + ) -> Result, String> { + // Check cache + { + let mut lock = cache.lock(); + let cached = lock.get(&self.key); + if let Some(account_id) = cached.and_then(|x| x.account_id.clone()) { + return Ok(account_id); } + } - if let MultiAddress::Address32(id) = address { - return Ok(Some(AccountId::from(id.clone()))); + let transparent = self.transparent()?; + let account_id = if let Preamble::Signed(address, _, _) = &transparent.preamble { + match address { + MultiAddress::Id(id) => Some(id.clone()), + MultiAddress::Address32(id) => Some(AccountId::from(id.clone())), + _ => None, } + } else { + None + }; + + // Set cache + { + let mut lock = cache.lock(); + let cached = lock.get_or_insert_mut(self.key, || Default::default()); + cached.account_id = Some(account_id.clone()); } - Ok(None) + Ok(account_id) } fn transparent(&mut self) -> Result<&TransparentOpaque, String> { @@ -484,8 +587,6 @@ impl ExtrinsicWrapper { } } -impl ExtrinsicWrapper {} - struct TransparentOpaque { pub bytes: Vec, pub call_start_pos: usize,