diff --git a/VERSION_COMPATIBILITY.md b/VERSION_COMPATIBILITY.md new file mode 100644 index 00000000..2341106d --- /dev/null +++ b/VERSION_COMPATIBILITY.md @@ -0,0 +1,208 @@ +# Version Compatibility Guide + +## Overview + +This document explains the version compatibility requirements for the Midnight Indexer components to help avoid configuration issues like those described in [Issue #597](https://github.com/midnightntwrk/midnight-indexer/issues/597). + +## Critical Rule: Never Use `latest` Tag + +**⚠️ IMPORTANT:** Never use the `latest` tag for any component in production or development environments. Always specify exact version numbers. + +Using `latest` can lead to: +- Incompatible component versions +- API endpoint mismatches (404 errors on `/api/v3/graphql`) +- Unpredictable behavior +- Difficult-to-debug issues + +## Version Requirements + +### All Indexer Components Must Match + +All three Indexer components **MUST** use the **exact same version**: + +- `chain-indexer` +- `wallet-indexer` +- `indexer-api` + +### Midnight Node Compatibility + +Each Indexer version requires a specific Midnight Node version. Check the [`NODE_VERSION`](./NODE_VERSION) file in the repository root for the compatible Node version. + +## Current Version Compatibility + +| Indexer Version | Compatible Node Version | +|----------------|------------------------| +| 3.0.0 | 0.18.0 | +| 3.0.0-alpha.19 | 0.18.0-rc.7 | + +## Example: Correct Configuration + +### Docker Compose + +```yaml +version: '3.8' + +services: + midnight-node: + image: midnightntwrk/midnight-node:0.18.0 # ✅ Exact version from NODE_VERSION + # ... other configuration + + chain-indexer: + image: midnightntwrk/chain-indexer:3.0.0 # ✅ Exact version + depends_on: + - midnight-node + # ... other configuration + + wallet-indexer: + image: midnightntwrk/wallet-indexer:3.0.0 # ✅ Same version as chain-indexer + depends_on: + - midnight-node + # ... other configuration + + indexer-api: + image: midnightntwrk/indexer-api:3.0.0 # ✅ Same version as other indexers + depends_on: + - chain-indexer + - wallet-indexer + ports: + - "3000:3000" + # ... other configuration +``` + +### Kubernetes + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: midnight-node +spec: + template: + spec: + containers: + - name: midnight-node + image: midnightntwrk/midnight-node:0.18.0 # ✅ Exact version +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: chain-indexer +spec: + template: + spec: + containers: + - name: chain-indexer + image: midnightntwrk/chain-indexer:3.0.0 # ✅ Exact version +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: wallet-indexer +spec: + template: + spec: + containers: + - name: wallet-indexer + image: midnightntwrk/wallet-indexer:3.0.0 # ✅ Same version +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: indexer-api +spec: + template: + spec: + containers: + - name: indexer-api + image: midnightntwrk/indexer-api:3.0.0 # ✅ Same version +``` + +## Common Issues and Solutions + +### Issue: 404 on `/api/v3/graphql` + +**Symptom:** +```bash +curl -X POST http://localhost:3000/api/v3/graphql +# Returns: HTTP/1.1 404 Not Found +``` + +**Cause:** Version mismatch between components, often due to using `latest` tag. + +**Solution:** +1. Check the `NODE_VERSION` file for the correct Midnight Node version +2. Ensure all Indexer components use the same exact version +3. Update your Docker Compose or Kubernetes configuration with exact versions +4. Restart all services + +### Issue: Different Author Values in Blocks Table + +**Symptom:** Two Indexers running on different servers produce different `author` values for the same blocks. + +**Cause:** This was a bug in versions prior to the fix for [Issue #548](https://github.com/midnightntwrk/midnight-indexer/issues/548). + +**Solution:** +1. Upgrade to the latest version that includes the fix +2. Ensure both Indexers use the exact same version +3. If needed, re-index from genesis to ensure consistency + +## Verification + +### Check Component Versions + +When the Indexer API starts, it will log version information: + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ Midnight Indexer API Version Info ║ +╠════════════════════════════════════════════════════════════════════════════╣ +║ Indexer API Version: 3.0.0 ║ +║ Expected Node Version: 0.18.0 ║ +╠════════════════════════════════════════════════════════════════════════════╣ +║ IMPORTANT: All Indexer components must use the SAME version: ║ +║ - chain-indexer:3.0.0 ║ +║ - wallet-indexer:3.0.0 ║ +║ - indexer-api:3.0.0 ║ +║ ║ +║ NEVER use 'latest' tag in production - always specify exact versions! ║ +╚════════════════════════════════════════════════════════════════════════════╝ +``` + +### Verify API Endpoints + +Test that the v3 API is available: + +```bash +# Should return GraphQL schema, not 404 +curl -X POST http://localhost:3000/api/v3/graphql \ + -H "Content-Type: application/json" \ + -d '{"query": "{ __schema { types { name } } }"}' +``` + +## Upgrading + +When upgrading to a new version: + +1. Check the `NODE_VERSION` file for the new compatible Node version +2. Update **all** components simultaneously to the same version +3. Update the Midnight Node to the compatible version +4. Test the upgrade in a non-production environment first +5. Verify all API endpoints are accessible + +## Support + +If you encounter version-related issues: + +1. Check this document for common solutions +2. Verify all versions match the requirements +3. Check the [GitHub Issues](https://github.com/midnightntwrk/midnight-indexer/issues) for similar problems +4. Open a new issue with: + - Exact versions of all components + - Configuration files (docker-compose.yml, etc.) + - Error messages and logs + +## References + +- [Issue #597: API v3 endpoint 404](https://github.com/midnightntwrk/midnight-indexer/issues/597) +- [Issue #548: Different author values](https://github.com/midnightntwrk/midnight-indexer/issues/548) +- [NODE_VERSION file](./NODE_VERSION) diff --git a/indexer-api/src/lib.rs b/indexer-api/src/lib.rs index 6b096b7c..3f5b5ca8 100644 --- a/indexer-api/src/lib.rs +++ b/indexer-api/src/lib.rs @@ -19,3 +19,5 @@ pub mod application; pub mod config; pub mod domain; pub mod infra; +#[cfg(feature = "cloud")] +pub mod version_validator; diff --git a/indexer-api/src/main.rs b/indexer-api/src/main.rs index ce99ea76..6f611e6e 100644 --- a/indexer-api/src/main.rs +++ b/indexer-api/src/main.rs @@ -56,6 +56,16 @@ async fn run() -> anyhow::Result<()> { // Load configuration. let config = Config::load().context("load configuration")?; info!(config:?; "starting"); + + // FIX #597: Log version information and validate compatibility + indexer_api::version_validator::log_version_info(); + // Note: Version validation would require additional infrastructure to query + // component versions. For now, we log the expected versions to help users + // identify mismatches. Future enhancement: query actual versions from components. + indexer_api::version_validator::validate_versions(None, None, None) + .unwrap_or_else(|err| { + warn!("Version validation warnings: {}", err); + }); let Config { run_migrations, application_config, diff --git a/indexer-api/src/version_validator.rs b/indexer-api/src/version_validator.rs new file mode 100644 index 00000000..e2079e68 --- /dev/null +++ b/indexer-api/src/version_validator.rs @@ -0,0 +1,252 @@ +// This file is part of midnight-indexer. +// Copyright (C) 2025 Midnight Foundation +// SPDX-License-Identifier: Apache-2.0 +// Licensed under the Apache License, Version 2.0 (the "License"); +// You may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Version validation module to ensure compatibility between Indexer components and Midnight Node. +//! +//! This module addresses issue #597 by providing validation and warnings when version +//! mismatches are detected, helping users avoid configuration errors. + +use log::{error, info, warn}; + +/// The expected Midnight Node version compatible with this Indexer version. +/// This value should match the content of the NODE_VERSION file in the repository root. +const EXPECTED_NODE_VERSION: &str = "0.18.0"; + +/// The current Indexer version. +const INDEXER_VERSION: &str = env!("CARGO_PKG_VERSION"); + +/// Validates version compatibility and logs warnings if issues are detected. +/// +/// This function should be called during application startup to help users +/// identify version mismatches early. +/// +/// # Arguments +/// +/// * `node_version` - Optional Midnight Node version string. If None, a warning is logged. +/// * `chain_indexer_version` - Optional chain-indexer version. If None, a warning is logged. +/// * `wallet_indexer_version` - Optional wallet-indexer version. If None, a warning is logged. +/// +/// # Returns +/// +/// Returns `Ok(())` if all versions are compatible, or an error message if critical +/// mismatches are detected. +pub fn validate_versions( + node_version: Option<&str>, + chain_indexer_version: Option<&str>, + wallet_indexer_version: Option<&str>, +) -> Result<(), String> { + info!( + indexer_api_version = INDEXER_VERSION, + expected_node_version = EXPECTED_NODE_VERSION; + "validating component versions" + ); + + let mut warnings = Vec::new(); + let mut errors = Vec::new(); + + // Validate Midnight Node version + match node_version { + Some(version) if version != EXPECTED_NODE_VERSION => { + let msg = format!( + "Midnight Node version mismatch: expected '{}', found '{}'. \ + This may cause API endpoint incompatibilities (see issue #597). \ + Check the NODE_VERSION file in the repository for the correct version.", + EXPECTED_NODE_VERSION, version + ); + warnings.push(msg); + } + None => { + warnings.push( + "Unable to determine Midnight Node version. \ + Please ensure you are using the correct Node version as specified in NODE_VERSION." + .to_string(), + ); + } + _ => { + info!( + node_version = node_version.unwrap(); + "Midnight Node version is compatible" + ); + } + } + + // Validate chain-indexer version + match chain_indexer_version { + Some(version) if version != INDEXER_VERSION => { + let msg = format!( + "chain-indexer version mismatch: expected '{}', found '{}'. \ + All Indexer components (chain-indexer, wallet-indexer, indexer-api) \ + must use the same version.", + INDEXER_VERSION, version + ); + errors.push(msg); + } + None => { + warnings.push( + "Unable to determine chain-indexer version. \ + Ensure all Indexer components use the same version." + .to_string(), + ); + } + _ => { + info!( + chain_indexer_version = chain_indexer_version.unwrap(); + "chain-indexer version is compatible" + ); + } + } + + // Validate wallet-indexer version + match wallet_indexer_version { + Some(version) if version != INDEXER_VERSION => { + let msg = format!( + "wallet-indexer version mismatch: expected '{}', found '{}'. \ + All Indexer components (chain-indexer, wallet-indexer, indexer-api) \ + must use the same version.", + INDEXER_VERSION, version + ); + errors.push(msg); + } + None => { + warnings.push( + "Unable to determine wallet-indexer version. \ + Ensure all Indexer components use the same version." + .to_string(), + ); + } + _ => { + info!( + wallet_indexer_version = wallet_indexer_version.unwrap(); + "wallet-indexer version is compatible" + ); + } + } + + // Log all warnings + for warning in &warnings { + warn!("{}", warning); + } + + // Log all errors and return error if any + if !errors.is_empty() { + for error_msg in &errors { + error!("{}", error_msg); + } + return Err(errors.join("\n")); + } + + if warnings.is_empty() { + info!("all component versions are compatible"); + } + + Ok(()) +} + +/// Logs a helpful message about version requirements. +/// +/// This should be called when the API starts to inform users about proper version usage. +pub fn log_version_info() { + info!( + "╔════════════════════════════════════════════════════════════════════════════╗" + ); + info!( + "║ Midnight Indexer API Version Info ║" + ); + info!( + "╠════════════════════════════════════════════════════════════════════════════╣" + ); + info!( + "║ Indexer API Version: {} ║", + INDEXER_VERSION + ); + info!( + "║ Expected Node Version: {} ║", + EXPECTED_NODE_VERSION + ); + info!( + "╠════════════════════════════════════════════════════════════════════════════╣" + ); + info!( + "║ IMPORTANT: All Indexer components must use the SAME version: ║" + ); + info!( + "║ - chain-indexer:{} ║", + INDEXER_VERSION + ); + info!( + "║ - wallet-indexer:{} ║", + INDEXER_VERSION + ); + info!( + "║ - indexer-api:{} ║", + INDEXER_VERSION + ); + info!( + "║ ║" + ); + info!( + "║ NEVER use 'latest' tag in production - always specify exact versions! ║" + ); + info!( + "║ ║" + ); + info!( + "║ For more information, see: ║" + ); + info!( + "║ https://github.com/midnightntwrk/midnight-indexer/blob/main/NODE_VERSION ║" + ); + info!( + "╚════════════════════════════════════════════════════════════════════════════╝" + ); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_validate_versions_all_compatible() { + let result = validate_versions( + Some(EXPECTED_NODE_VERSION), + Some(INDEXER_VERSION), + Some(INDEXER_VERSION), + ); + assert!(result.is_ok()); + } + + #[test] + fn test_validate_versions_node_mismatch() { + let result = validate_versions(Some("0.17.0"), Some(INDEXER_VERSION), Some(INDEXER_VERSION)); + // Should return Ok but log warnings + assert!(result.is_ok()); + } + + #[test] + fn test_validate_versions_indexer_mismatch() { + let result = validate_versions( + Some(EXPECTED_NODE_VERSION), + Some("2.0.0"), + Some(INDEXER_VERSION), + ); + // Should return Err due to indexer version mismatch + assert!(result.is_err()); + } + + #[test] + fn test_validate_versions_missing_versions() { + let result = validate_versions(None, None, None); + // Should return Ok but log warnings + assert!(result.is_ok()); + } +}