use ghost_traits::networks::NetworkRpcResolver; use serde::{Deserialize, Deserializer}; use sp_std::vec; use serde_json::json; use sp_std::vec::Vec; use sp_core::{H256, U256}; use sp_runtime::offchain::http::{Error as HttpError, HttpResult, PendingRequest}; use crate::{ ExternalBlockNumber, NetworkType, RequestId, RpcResolverError, WeavingError, WeavingSession, LOG_TARGET, }; fn parse_response( http_result: HttpResult, request_id: RequestId, network_type: &NetworkType, ) -> Result { let response = http_result.map_err(|err| match err { HttpError::DeadlineReached => RpcResolverError::DeadlineReached, HttpError::IoError => RpcResolverError::IoError, HttpError::Unknown => RpcResolverError::Unknown, })?; if response.code != 200 { return Err(RpcResolverError::Non200HttpCode); } let response_body = response.body().collect::>(); let response_str = sp_std::str::from_utf8(&response_body) .map_err(|_| RpcResolverError::UnparsableResponseBody)?; let response_data: NetworkResponse = match network_type { NetworkType::Evm => { let evm_response: EvmResponse = serde_json::from_str(response_str) .map_err(|_| RpcResolverError::JsonDeserializationFailed)?; evm_response.into() } NetworkType::Utxo => { let utxo_response: UtxoResponse = serde_json::from_str(response_str) .map_err(|_| RpcResolverError::JsonDeserializationFailed)?; utxo_response.into() } _ => return Err(RpcResolverError::UnknownNetworkType), }; if request_id != response_data.id { return Err(RpcResolverError::InvalidResponseId); } if response_data.error.is_some() { return Err(RpcResolverError::RpcProtocolError); } response_data .result .ok_or(RpcResolverError::RpcEmptyResultField) } fn parse_pending_requests<'a, R: Copy + Into + 'static>( pending_requests: Vec, deadline: sp_runtime::offchain::Timestamp, request_id: R, network_type: &'a NetworkType, ) -> impl Iterator + 'a { PendingRequest::try_wait_all(pending_requests, Some(deadline)) .into_iter() .filter_map(move |wait_result| { let http_result = wait_result .inspect_err(|_| { log::error!( target: LOG_TARGET, "🕸️ Error during request preparation: {:?}", WeavingError::::DeadlineReached, ) }) .ok()?; parse_response(http_result, request_id.into(), network_type) .inspect_err(|err| { log::error!( target: LOG_TARGET, "🕸️ Error during response parsing: {:?}", err, ) }) .ok() }) } impl NetworkRpcResolver, ExternalBlockNumber, H256, RequestId, WeavingSession> for NetworkType { fn get_block_request_body(&self, id: u64) -> Vec { let method = match self { NetworkType::Evm => "eth_blockNumber", NetworkType::Utxo => "getblockcount", _ => "unknown", }; serde_json::to_vec(&json!({"id": id, "jsonrpc": "2.0", "method": method})) .unwrap_or_else(|_| b"{}".to_vec()) } fn get_hash_request_body( &self, id: u64, target_block: ExternalBlockNumber, weaving_session: WeavingSession, contract_ref: &[u8], selector_ref: &[u8], ) -> Vec { extern crate alloc; let (method, params) = match self { NetworkType::Evm => { let contract_hex = hex::encode(contract_ref); let selector_hex = hex::encode(selector_ref); let encoded_session = alloc::format!("{:064x}", weaving_session); let encoded_block = alloc::format!("{:064x}", target_block); let to_field = alloc::format!("0x{}", contract_hex); let data_field = alloc::format!("0x{}{}{}", selector_hex, encoded_session, encoded_block,); ( "eth_call", json!([{"to": to_field, "data": data_field}, "latest"]), ) } NetworkType::Utxo => ("getblockhash", json!([target_block])), _ => ("unknown", json!([])), }; serde_json::to_vec(&json!({"id": id, "jsonrpc": "2.0", "method": method, "params": params})) .unwrap_or_else(|_| b"{}".to_vec()) } fn parse_block_requests( &self, pending_requests: Vec, deadline: sp_runtime::offchain::Timestamp, request_id: RequestId, ) -> Vec { parse_pending_requests(pending_requests, deadline, request_id, self) .filter_map(|response| match response { NetworkResponseType::BlockNumber(block) => Some(block), NetworkResponseType::TreeRoot(_, _) => None, }) .collect() } fn parse_hash_requests( &self, pending_requests: Vec, deadline: sp_runtime::offchain::Timestamp, request_id: RequestId, ) -> Vec<(H256, WeavingSession)> { parse_pending_requests(pending_requests, deadline, request_id, self) .filter_map(|response| match response { NetworkResponseType::TreeRoot(hash, next) => Some((hash, next)), NetworkResponseType::BlockNumber(_) => None, }) .collect() } } #[derive(Clone, PartialEq, Deserialize)] struct NetworkResponse { pub id: RequestId, pub error: Option, pub result: Option, } impl From for NetworkResponse { fn from(evm_response: EvmResponse) -> Self { Self { id: evm_response.id, error: evm_response.error, result: evm_response.result.map(Into::into), } } } impl From for NetworkResponse { fn from(utxo_response: UtxoResponse) -> Self { Self { id: utxo_response.id, error: utxo_response.error, result: utxo_response.result.map(Into::into), } } } #[derive(Clone, PartialEq, Deserialize)] enum NetworkResponseType { BlockNumber(ExternalBlockNumber), TreeRoot(H256, WeavingSession), } impl From for NetworkResponseType { fn from(evm_type: EvmResponseType) -> Self { match evm_type { EvmResponseType::BlockNumber(block) => Self::BlockNumber(block), EvmResponseType::TreeRoot(root, next) => Self::TreeRoot(root, next), } } } impl From for NetworkResponseType { fn from(utxo_type: UtxoResponseType) -> Self { match utxo_type { UtxoResponseType::BlockNumber(block) => Self::BlockNumber(block), UtxoResponseType::TreeRoot(root) => Self::TreeRoot(root, WeavingSession::MAX), } } } #[derive(Deserialize, Eq, PartialEq, Clone)] struct EvmResponse { pub id: RequestId, pub error: Option, pub result: Option, } #[derive(Deserialize, Eq, PartialEq, Clone)] struct UtxoResponse { pub id: RequestId, pub error: Option, pub result: Option, } #[derive(Deserialize, Clone, PartialEq, Eq)] #[serde(untagged)] enum EvmResponseType { #[serde(deserialize_with = "de_hex_string_to_block_number")] BlockNumber(ExternalBlockNumber), #[serde(deserialize_with = "de_contract_tuple")] TreeRoot(H256, WeavingSession), } #[derive(Deserialize, Clone, PartialEq, Eq)] #[serde(untagged)] enum UtxoResponseType { BlockNumber(ExternalBlockNumber), #[serde(deserialize_with = "de_string_to_h256")] TreeRoot(H256), } fn de_hex_string_to_block_number<'de, D>(de: D) -> Result where D: Deserializer<'de>, { let s: &'de str = Deserialize::deserialize(de)?; let clean_hex = s.strip_prefix("0x").unwrap_or(s); let trimmed_hex = clean_hex.trim_start_matches('0'); if trimmed_hex.is_empty() { return Ok(Default::default()); } ExternalBlockNumber::from_str_radix(trimmed_hex, 16) .map_err(|_| serde::de::Error::custom("Failed to parse block number hex")) } fn de_string_to_h256<'de, D>(de: D) -> Result where D: Deserializer<'de>, { let s: &'de str = Deserialize::deserialize(de)?; let clean_hex = s.strip_prefix("0x").unwrap_or(s); let mut bytes = [0u8; 32]; hex::decode_to_slice(clean_hex, &mut bytes) .map_err(|_| serde::de::Error::custom("Failed to parse hex string into H256"))?; Ok(H256::from(bytes)) } fn de_contract_tuple<'de, D>(de: D) -> Result<(H256, WeavingSession), D::Error> where D: Deserializer<'de>, { let s: &str = Deserialize::deserialize(de)?; let clean_hex = s.strip_prefix("0x").unwrap_or(s); if clean_hex.len() != 128 { return Err(serde::de::Error::custom( "Invalid hex string length for (bytes32, uint256)", )); } let (bytes32_hex, uint256_hex) = clean_hex.split_at(64); let mut bytes = [0u8; 32]; hex::decode_to_slice(bytes32_hex, &mut bytes) .map_err(|_| serde::de::Error::custom("Failed to parse hex string into H256"))?; let root_hash = H256::from(bytes); let uint256_val = U256::from_str_radix(uint256_hex, 16) .map_err(|_| serde::de::Error::custom("Failed to parse uint256"))?; let next_weaving_session: WeavingSession = uint256_val .try_into() .map_err(|_| serde::de::Error::custom("EVM uint256 value overflow bounds"))?; Ok((root_hash, next_weaving_session)) } #[cfg(test)] mod parsing_tests { use super::*; use serde::Deserialize; use sp_core::H256; #[derive(Deserialize)] struct TestBlock { #[serde(deserialize_with = "de_hex_string_to_block_number")] val: ExternalBlockNumber, } #[derive(Deserialize)] struct TestHash { #[serde(deserialize_with = "de_string_to_h256")] val: H256, } #[derive(Deserialize)] struct TestTuple { #[serde(deserialize_with = "de_contract_tuple")] val: (H256, WeavingSession), } #[test] fn test_de_hex_string_to_block_number_success() { let json = r#"{"val": "0x64"}"#; let parsed: TestBlock = serde_json::from_str(json).unwrap(); assert_eq!(parsed.val, 100); let json_zeros = r#"{"val": "0x00000a"}"#; let parsed_zeros: TestBlock = serde_json::from_str(json_zeros).unwrap(); assert_eq!(parsed_zeros.val, 10); let json_zero = r#"{"val": "0x0"}"#; let parsed_zero: TestBlock = serde_json::from_str(json_zero).unwrap(); assert_eq!(parsed_zero.val, 0); } #[test] fn test_de_hex_string_to_block_number_invalid() { let json = r#"{"val": "0x6G"}"#; let result: Result = serde_json::from_str(json); assert!(result.is_err()); } #[test] fn test_de_string_to_h256_success() { let raw_hash = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; let json = format!(r#"{{"val": "0x{}"}}"#, raw_hash); let parsed: TestHash = serde_json::from_str(&json).unwrap(); assert_eq!(parsed.val, H256::repeat_byte(0xAA)); } #[test] fn test_de_string_to_h256_invalid_length() { let json = r#"{"val": "0x00aa"}"#; let result: Result = serde_json::from_str(json); assert!(result.is_err()); } #[test] fn test_de_contract_tuple_success() { let hash_part = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; let session_part = "000000000000000000000000000000000000000000000000000000000000002a"; let json = format!(r#"{{"val": "0x{}{}"}}"#, hash_part, session_part); let parsed: TestTuple = serde_json::from_str(&json).unwrap(); assert_eq!(parsed.val.0, H256::repeat_byte(0xBB)); assert_eq!(parsed.val.1, 42u64); } #[test] fn test_de_contract_tuple_invalid_length() { let json = r#"{"val": "0xbbbb002a"}"#; let result: Result = serde_json::from_str(json); assert!(result.is_err()); let err_msg = result.err().unwrap().to_string(); assert!(err_msg.contains("Invalid hex string length")); } #[test] fn test_de_contract_tuple_overflow() { let hash_part = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; let session_overflow = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; let json = format!(r#"{{"val": "0x{}{}"}}"#, hash_part, session_overflow); let result: Result = serde_json::from_str(&json); assert!(result.is_err()); let err_msg = result.err().unwrap().to_string(); assert!(err_msg.contains("EVM uint256 value overflow bounds")); } }