diff --git a/CHANGELOG.md b/CHANGELOG.md index 6474ac085986..99827baffec9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,8 @@ - [#6531](https://github.com/ChainSafe/forest/issues/6531): `Filecoin.EthGetBlockByHash` now works with `lotus-gateway`. +- [#6552](https://github.com/ChainSafe/forest/issues/6552): `Filecoin.ChainGetTipset` now works with `lotus-gateway`. + - [#6535](https://github.com/ChainSafe/forest/pull/6535): Fixed incorrect replace by fee behavior when at limits of pending messages in mempool. - [#6541](https://github.com/ChainSafe/forest/pull/6541): Fixed "actor not found" errors when running Foundry (forge) scripts. The `eth_getBalance`, `eth_getTransactionCount`, and `eth_getCode` methods now return default values (0 balance, 0 nonce, empty code) for non-existent addresses, matching Lotus and standard Ethereum behavior. diff --git a/scripts/tests/api_compare/docker-compose.yml b/scripts/tests/api_compare/docker-compose.yml index f4af8a385aca..6ac3957b9966 100644 --- a/scripts/tests/api_compare/docker-compose.yml +++ b/scripts/tests/api_compare/docker-compose.yml @@ -229,8 +229,9 @@ services: command: - | set -uxo pipefail - LOTUS_API_INFO="$(cat /data/lotus-token):/dns/lotus/tcp/${LOTUS_RPC_PORT}/http" - FOREST_API_INFO="$(cat /data/forest-token):/dns/forest/tcp/${FOREST_RPC_PORT}/http" + # Test against websocket endpoint for online server + LOTUS_API_INFO="$(cat /data/lotus-token):/dns/lotus/tcp/${LOTUS_RPC_PORT}/ws" + FOREST_API_INFO="$(cat /data/forest-token):/dns/forest/tcp/${FOREST_RPC_PORT}/ws" forest-tool api compare $(ls /data/*.car.zst | tail -n 1) \ --forest $$FOREST_API_INFO \ --lotus $$LOTUS_API_INFO \ @@ -273,6 +274,7 @@ services: command: - | set -uxo pipefail + # Test against http endpoint for offline server LOTUS_API_INFO="$(cat /data/lotus-token):/dns/lotus/tcp/${LOTUS_RPC_PORT}/http" FOREST_API_INFO="$(cat /data/forest-token-offline):/dns/api-serve/tcp/${FOREST_OFFLINE_RPC_PORT}/http" forest-tool api compare $(ls /data/*.car.zst | tail -n 1) \ diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 250732410637..b6c1eabdb6bc 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -842,7 +842,7 @@ mod tests { let handle = tokio::spawn(start_rpc(state, rpc_listener, stop_handle, None)); - // Send a few requests + // Send a few http requests let client = Client::from_url( format!("http://{}:{}/", rpc_address.ip(), rpc_address.port()) @@ -860,6 +860,19 @@ mod tests { assert_eq!(response.block_delay, block_delay_secs); assert_eq!(response.api_version, ShiftingVersion::new(2, 3, 0)); + let response = super::methods::auth::AuthVerify::call(&client, (jwt_read.clone(),)) + .await + .unwrap(); + assert_eq!(response, jwt_read_permissions); + + // Send a few websocket requests + + let client = Client::from_url( + format!("ws://{}:{}/", rpc_address.ip(), rpc_address.port()) + .parse() + .unwrap(), + ); + let response = super::methods::auth::AuthVerify::call(&client, (jwt_read,)) .await .unwrap(); diff --git a/src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap b/src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap index 8c05524bd277..97057de25d9c 100644 --- a/src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap +++ b/src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap @@ -2040,8 +2040,6 @@ components: anyOf: - $ref: "#/components/schemas/TipsetTag" - type: "null" - required: - - key TipsetHeight: type: object properties: diff --git a/src/rpc/types/tipset_selector.rs b/src/rpc/types/tipset_selector.rs index e9dc3a8f3bb0..c3f0632ee8bd 100644 --- a/src/rpc/types/tipset_selector.rs +++ b/src/rpc/types/tipset_selector.rs @@ -54,7 +54,7 @@ impl TipsetSelector { pub struct TipsetHeight { pub at: ChainEpoch, pub previous: bool, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub anchor: Option, } lotus_json_with_self!(TipsetHeight); @@ -82,10 +82,14 @@ impl TipsetHeight { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] pub struct TipsetAnchor { - #[serde(with = "crate::lotus_json")] + #[serde( + with = "crate::lotus_json", + skip_serializing_if = "ApiTipsetKey::is_none", + default + )] #[schemars(with = "LotusJson")] pub key: ApiTipsetKey, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub tag: Option, } lotus_json_with_self!(TipsetAnchor); diff --git a/src/tool/subcommands/api_cmd/api_compare_tests.rs b/src/tool/subcommands/api_cmd/api_compare_tests.rs index afe71fc6c681..3c26e0f8ee82 100644 --- a/src/tool/subcommands/api_cmd/api_compare_tests.rs +++ b/src/tool/subcommands/api_cmd/api_compare_tests.rs @@ -58,6 +58,7 @@ use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serde_json::Value; use similar::{ChangeTag, TextDiff}; +use std::borrow::Cow; use std::path::Path; use std::time::Instant; use std::{ @@ -168,7 +169,12 @@ impl TestSummary { match err { rpc::ClientError::Call(it) => match it.code().into() { ErrorCode::MethodNotFound => Self::MissingMethod, - _ => Self::Rejected(it.message().to_string()), + _ => { + // `lotus-gateway` adds `RPC error (-32603):` prefix to the error message that breaks tests, + // normalize the error message first + let message = normalized_error_message(it.message()); + Self::Rejected(message.to_string()) + } }, rpc::ClientError::ParseError(_) => Self::NotJsonRPC, rpc::ClientError::RequestTimeout => Self::Timeout, @@ -3063,6 +3069,12 @@ fn evaluate_test_success( } } +fn normalized_error_message(s: &str) -> Cow<'_, str> { + // remove `RPC error (-32603):` prefix added by `lotus-gateway` + let lotus_gateway_error_prefix = lazy_regex::regex!(r#"^RPC\serror\s\(-?\d+\):\s*"#); + lotus_gateway_error_prefix.replace(s, "") +} + /// Dump test data to the specified directory fn dump_test_data(dump_dir: &Path, success: bool, test_dump: &TestDump) -> anyhow::Result<()> { let dir = dump_dir.join(if success { "valid" } else { "invalid" }); @@ -3108,3 +3120,28 @@ fn validate_tagged_tipset_v2(req: rpc::Request>, offline: bool) - _ => false, }) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_normalized_error_message_1() { + let s = "RPC error (-32603): exactly one tipset selection criteria must be specified"; + let r = normalized_error_message(s); + assert_eq!( + r.as_ref(), + "exactly one tipset selection criteria must be specified" + ); + } + + #[test] + fn test_normalized_error_message_2() { + let s = "exactly one tipset selection criteria must be specified"; + let r = normalized_error_message(s); + assert_eq!( + r.as_ref(), + "exactly one tipset selection criteria must be specified" + ); + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 07478b8f652f..d724536e4c20 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -93,10 +93,11 @@ fn test_url_from_multiaddr() { #[track_caller] fn do_test(input: &str, expected: &str) { let UrlFromMultiAddr(url) = input.parse().unwrap(); - assert_eq!(url.as_str(), expected); + assert_eq!(url.as_str(), expected, "input: {input}"); } do_test("/dns/example.com/http", "http://example.com/"); do_test("/dns/example.com/tcp/8080/http", "http://example.com:8080/"); + do_test("/dns/example.com/tcp/8081/ws", "ws://example.com:8081/"); do_test("/ip4/127.0.0.1/wss", "wss://127.0.0.1/"); // with password