ghost-node/pallets/weaver/src/impls.rs
Uncle Stretch d83ee59508
let the EXODUS begin...
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2026-08-29 14:48:50 +03:00

413 lines
13 KiB
Rust

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<NetworkResponseType, RpcResolverError> {
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::<Vec<u8>>();
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<RequestId> + 'static>(
pending_requests: Vec<PendingRequest>,
deadline: sp_runtime::offchain::Timestamp,
request_id: R,
network_type: &'a NetworkType,
) -> impl Iterator<Item = NetworkResponseType> + '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::<u32>::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<WeavingError<u32>, ExternalBlockNumber, H256, RequestId, WeavingSession>
for NetworkType
{
fn get_block_request_body(&self, id: u64) -> Vec<u8> {
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<u8> {
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<PendingRequest>,
deadline: sp_runtime::offchain::Timestamp,
request_id: RequestId,
) -> Vec<ExternalBlockNumber> {
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<PendingRequest>,
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<serde_json::Value>,
pub result: Option<NetworkResponseType>,
}
impl From<EvmResponse> 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<UtxoResponse> 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<EvmResponseType> 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<UtxoResponseType> 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<serde_json::Value>,
pub result: Option<EvmResponseType>,
}
#[derive(Deserialize, Eq, PartialEq, Clone)]
struct UtxoResponse {
pub id: RequestId,
pub error: Option<serde_json::Value>,
pub result: Option<UtxoResponseType>,
}
#[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<ExternalBlockNumber, D::Error>
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<H256, D::Error>
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<TestBlock, _> = 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<TestHash, _> = 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<TestTuple, _> = 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<TestTuple, _> = 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"));
}
}