diff --git a/substrate/client/serai/src/lib.rs b/substrate/client/serai/src/lib.rs index 6797a36f..d29c12d3 100644 --- a/substrate/client/serai/src/lib.rs +++ b/substrate/client/serai/src/lib.rs @@ -134,25 +134,29 @@ impl Serai { } async fn block_internal( - block: impl Future>, - ) -> Result { + block: impl Future, RpcError>>, + ) -> Result, RpcError> { let bin = block.await?; - Block::deserialize( - &mut hex::decode(&bin) - .map_err(|_| RpcError::InvalidNode("node returned non-hex-encoded block".to_string()))? - .as_slice(), - ) - .map_err(|_| RpcError::InvalidNode("node returned invalid block".to_string())) + bin + .map(|bin| { + Block::deserialize( + &mut hex::decode(&bin) + .map_err(|_| RpcError::InvalidNode("node returned non-hex-encoded block".to_string()))? + .as_slice(), + ) + .map_err(|_| RpcError::InvalidNode("node returned invalid block".to_string())) + }) + .transpose() } /// Fetch a block from the Serai blockchain. - pub async fn block(&self, block: BlockHash) -> Result { + pub async fn block(&self, block: BlockHash) -> Result, RpcError> { Self::block_internal(self.call("blockchain/block", &format!(r#"{{ "block": "{block}" }}"#))) .await } /// Fetch a block from the Serai blockchain by its number. - pub async fn block_by_number(&self, block: u64) -> Result { + pub async fn block_by_number(&self, block: u64) -> Result, RpcError> { Self::block_internal(self.call("blockchain/block", &format!(r#"{{ "block": {block} }}"#))).await } diff --git a/substrate/client/serai/tests/blockchain.rs b/substrate/client/serai/tests/blockchain.rs index 0c9a3bbf..8c9b614d 100644 --- a/substrate/client/serai/tests/blockchain.rs +++ b/substrate/client/serai/tests/blockchain.rs @@ -49,8 +49,8 @@ async fn blockchain() { let test_finalized_block = |number| { let serai = &serai; async move { - let block = serai.block_by_number(number).await.unwrap(); - assert_eq!(serai.block(block.header.hash()).await.unwrap(), block); + let block = serai.block_by_number(number).await.unwrap().unwrap(); + assert_eq!(serai.block(block.header.hash()).await.unwrap().unwrap(), block); assert!(serai.finalized(block.header.hash()).await.unwrap()); } }; @@ -70,7 +70,7 @@ async fn blockchain() { continue; }; // Check if it's considered finalized - let considered_finalized = serai.finalized(block.header.hash()).await.unwrap(); + let considered_finalized = serai.finalized(block.unwrap().header.hash()).await.unwrap(); // Ensure the finalized block is the same, meaning this block didn't become finalized as // we made these RPC requests if latest_finalized != serai.latest_finalized_block_number().await.unwrap() { @@ -108,7 +108,7 @@ async fn blockchain() { let mut observed_consensus_commitments = HashSet::new(); let mut tagged_block_hashes = vec![]; for i in 0 ..= last_block_number { - let block = serai.block_by_number(i).await.unwrap(); + let block = serai.block_by_number(i).await.unwrap().unwrap(); assert_eq!(block.header.number(), i); diff --git a/substrate/client/serai/tests/validator_sets.rs b/substrate/client/serai/tests/validator_sets.rs index cc9ca300..522f3e45 100644 --- a/substrate/client/serai/tests/validator_sets.rs +++ b/substrate/client/serai/tests/validator_sets.rs @@ -53,7 +53,7 @@ async fn validator_sets() { // The genesis block should have the expected events { let mut events = serai - .as_of(serai.block_by_number(0).await.unwrap().header.hash()) + .as_of(serai.block_by_number(0).await.unwrap().unwrap().header.hash()) .await .unwrap() .validator_sets() @@ -98,7 +98,7 @@ async fn validator_sets() { assert_eq!( serai - .as_of(serai.block_by_number(0).await.unwrap().header.hash()) + .as_of(serai.block_by_number(0).await.unwrap().unwrap().header.hash()) .await .unwrap() .validator_sets() @@ -115,7 +115,7 @@ async fn validator_sets() { { assert_eq!( serai - .as_of(serai.block_by_number(1).await.unwrap().header.hash()) + .as_of(serai.block_by_number(1).await.unwrap().unwrap().header.hash()) .await .unwrap() .validator_sets() @@ -126,7 +126,7 @@ async fn validator_sets() { ); assert_eq!( serai - .as_of(serai.block_by_number(1).await.unwrap().header.hash()) + .as_of(serai.block_by_number(1).await.unwrap().unwrap().header.hash()) .await .unwrap() .validator_sets() @@ -138,8 +138,10 @@ async fn validator_sets() { } { - let serai = - serai.as_of(serai.block_by_number(0).await.unwrap().header.hash()).await.unwrap(); + let serai = serai + .as_of(serai.block_by_number(0).await.unwrap().unwrap().header.hash()) + .await + .unwrap(); let serai = serai.validator_sets(); for network in NetworkId::all() { match network { diff --git a/substrate/node/src/rpc/blockchain.rs b/substrate/node/src/rpc/blockchain.rs index 58621eec..e0cdb868 100644 --- a/substrate/node/src/rpc/blockchain.rs +++ b/substrate/node/src/rpc/blockchain.rs @@ -37,10 +37,12 @@ pub(crate) fn module< module.register_method( "blockchain/is_finalized", |params, client, _ext| -> Result<_, Error> { - let block_hash = block_hash(&**client, ¶ms)?; + let Some(block_hash) = block_hash(&**client, ¶ms)? else { + return Ok(false); + }; let finalized = client.info().finalized_number; let Ok(Some(number)) = client.number(block_hash) else { - Err(Error::Missing("failed to fetch block's number"))? + return Ok(false); }; let Ok(status) = client.block_status(block_hash) else { Err(Error::Internal("failed to fetch block's status"))? @@ -53,18 +55,22 @@ pub(crate) fn module< )?; module.register_method("blockchain/block", |params, client, _ext| -> Result<_, Error> { - let block_hash = block_hash(&**client, ¶ms)?; + let Some(block_hash) = block_hash(&**client, ¶ms)? else { + return Ok(None); + }; let Ok(Some(block)) = client.block(block_hash) else { - Err(Error::Missing("couldn't find requested block"))? + return Ok(None); }; - Ok(hex::encode(borsh::to_vec(&serai_abi::Block::from(block.block)).unwrap())) + Ok(Some(hex::encode(borsh::to_vec(&serai_abi::Block::from(block.block)).unwrap()))) })?; module.register_method("blockchain/events", |params, client, _ext| -> Result<_, Error> { - let block_hash = block_hash(&**client, ¶ms)?; + let Some(block_hash) = block_hash(&**client, ¶ms)? else { + Err(Error::InvalidStateReference)? + }; let Ok(events) = client.runtime_api().events(block_hash) else { - Err(Error::Missing("couldn't fetch the events for the requested block"))? + Err(Error::InvalidStateReference)? }; Ok( events diff --git a/substrate/node/src/rpc/utils.rs b/substrate/node/src/rpc/utils.rs index dc164ae2..998ff588 100644 --- a/substrate/node/src/rpc/utils.rs +++ b/substrate/node/src/rpc/utils.rs @@ -6,7 +6,7 @@ use serai_abi::{primitives::prelude::*, SubstrateBlock as Block}; pub(super) enum Error { Internal(&'static str), InvalidRequest(&'static str), - Missing(&'static str), + InvalidStateReference, } impl From for jsonrpsee::types::error::ErrorObjectOwned { @@ -18,9 +18,11 @@ impl From for jsonrpsee::types::error::ErrorObjectOwned { Error::InvalidRequest(str) => { jsonrpsee::types::error::ErrorObjectOwned::owned(-2, str, Option::<()>::None) } - Error::Missing(str) => { - jsonrpsee::types::error::ErrorObjectOwned::owned(-3, str, Option::<()>::None) - } + Error::InvalidStateReference => jsonrpsee::types::error::ErrorObjectOwned::owned( + -4, + "the block used as the reference was not locally held", + Option::<()>::None, + ), } } } @@ -30,7 +32,7 @@ pub(super) fn block_hash< >( client: &C, params: &jsonrpsee::types::params::Params, -) -> Result<::Hash, Error> { +) -> Result::Hash>, Error> { #[derive(sp_core::serde::Deserialize)] #[serde(crate = "sp_core::serde")] struct BlockByHash { @@ -50,13 +52,13 @@ pub(super) fn block_hash< }) else { return Err(Error::InvalidRequest("requested block hash wasn't a valid hash")); }; - block_hash + Some(block_hash) } else { let Ok(block_number) = params.parse::() else { return Err(Error::InvalidRequest("requested block wasn't a valid hash nor number")); }; - let Ok(Some(block_hash)) = client.block_hash(block_number.block) else { - return Err(Error::Missing("no block hash for that block number")); + let Ok(block_hash) = client.block_hash(block_number.block) else { + return Err(Error::Internal("couldn't fetch block hash for block number")); }; block_hash }) diff --git a/substrate/node/src/rpc/validator_sets.rs b/substrate/node/src/rpc/validator_sets.rs index ba23069f..70ee006b 100644 --- a/substrate/node/src/rpc/validator_sets.rs +++ b/substrate/node/src/rpc/validator_sets.rs @@ -72,7 +72,9 @@ pub(crate) fn module< module.register_method( "validator-sets/current_session", |params, client, _ext| -> Result<_, Error> { - let block_hash = block_hash(&**client, ¶ms)?; + let Some(block_hash) = block_hash(&**client, ¶ms)? else { + Err(Error::InvalidStateReference)? + }; let network = network(¶ms)?; let Ok(session) = client.runtime_api().current_session(block_hash, network) else { Err(Error::Internal("couldn't fetch the session for the requested network"))? @@ -84,7 +86,9 @@ pub(crate) fn module< module.register_method( "validator-sets/current_stake", |params, client, _ext| -> Result<_, Error> { - let block_hash = block_hash(&**client, ¶ms)?; + let Some(block_hash) = block_hash(&**client, ¶ms)? else { + Err(Error::InvalidStateReference)? + }; let network = network(¶ms)?; let Ok(stake) = client.runtime_api().current_stake(block_hash, network) else { Err(Error::Internal("couldn't fetch the total allocated stake for the requested network"))? @@ -94,7 +98,9 @@ pub(crate) fn module< ); module.register_method("validator-sets/keys", |params, client, _ext| -> Result<_, Error> { - let block_hash = block_hash(&**client, ¶ms)?; + let Some(block_hash) = block_hash(&**client, ¶ms)? else { + Err(Error::InvalidStateReference)? + }; let set = set(¶ms)?; let Ok(set) = ExternalValidatorSet::try_from(set) else { Err(Error::InvalidRequest("requested keys for a non-external validator set"))?