From 543322b5da733b505446250e36a53c851db18830 Mon Sep 17 00:00:00 2001 From: glihm Date: Thu, 16 May 2024 16:36:38 +0200 Subject: [PATCH] fix: add more logic on semver check of Starknet API (#1966) * fix: add more logic on semver check of Starknet API * fix: use matches functionality from semver crate * fix: fix typo --- bin/sozo/src/commands/migrate.rs | 70 +++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/bin/sozo/src/commands/migrate.rs b/bin/sozo/src/commands/migrate.rs index e4ef27b94f..9cbd421951 100644 --- a/bin/sozo/src/commands/migrate.rs +++ b/bin/sozo/src/commands/migrate.rs @@ -123,12 +123,12 @@ pub async fn setup_env<'a>( let (account, rpc_url) = { let provider = starknet.provider(env)?; - trace!("Provider initialized."); + trace!(?provider, "Provider initialized."); let spec_version = provider.spec_version().await?; trace!(spec_version); - if spec_version != RPC_SPEC_VERSION { + if !is_compatible_version(&spec_version, RPC_SPEC_VERSION)? { return Err(anyhow!( "Unsupported Starknet RPC version: {}, expected {}.", spec_version, @@ -167,3 +167,69 @@ pub async fn setup_env<'a>( Ok((world_address, account, rpc_url.to_string())) } + +/// Checks if the provided version string is compatible with the expected version string using +/// semantic versioning rules. Includes specific backward compatibility rules, e.g., version 0.6 is +/// compatible with 0.7. +/// +/// # Arguments +/// +/// * `provided_version` - The version string provided by the user. +/// * `expected_version` - The expected version string. +/// +/// # Returns +/// +/// * `Result` - Returns `true` if the provided version is compatible with the expected +/// version, `false` otherwise. +fn is_compatible_version(provided_version: &str, expected_version: &str) -> Result { + use semver::{Version, VersionReq}; + + let provided_ver = Version::parse(provided_version) + .map_err(|e| anyhow!("Failed to parse provided version '{}': {}", provided_version, e))?; + let expected_ver = Version::parse(expected_version) + .map_err(|e| anyhow!("Failed to parse expected version '{}': {}", expected_version, e))?; + + // Specific backward compatibility rule: 0.6 is compatible with 0.7. + if (provided_ver.major == 0 && provided_ver.minor == 6) + && (expected_ver.major == 0 && expected_ver.minor == 7) + { + return Ok(true); + } + + let expected_ver_req = VersionReq::parse(expected_version).map_err(|e| { + anyhow!("Failed to parse expected version requirement '{}': {}", expected_version, e) + })?; + + Ok(expected_ver_req.matches(&provided_ver)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_is_compatible_version_major_mismatch() { + assert!(!is_compatible_version("1.0.0", "2.0.0").unwrap()); + } + + #[test] + fn test_is_compatible_version_minor_compatible() { + assert!(is_compatible_version("1.2.0", "1.1.0").unwrap()); + } + + #[test] + fn test_is_compatible_version_minor_mismatch() { + assert!(!is_compatible_version("0.2.0", "0.7.0").unwrap()); + } + + #[test] + fn test_is_compatible_version_specific_backward_compatibility() { + assert!(is_compatible_version("0.6.0", "0.7.1").unwrap()); + } + + #[test] + fn test_is_compatible_version_invalid_version_string() { + assert!(is_compatible_version("1.0", "1.0.0").is_err()); + assert!(is_compatible_version("1.0.0", "1.0").is_err()); + } +}