sui_package_management/
system_package_versions.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{collections::BTreeMap, sync::LazyLock};
5
6use anyhow::Context;
7use sui_protocol_config::ProtocolVersion;
8
9/// Static mapping from protocol versions to the metadata for the system packages
10// Generated by [generate_system_packages_version_table] in build.rs
11static VERSION_TABLE: LazyLock<BTreeMap<ProtocolVersion, SystemPackagesVersion>> =
12    LazyLock::new(|| {
13        BTreeMap::from(include!(concat!(
14            env!("OUT_DIR"),
15            "/system_packages_version_table.rs"
16        )))
17    });
18
19pub const SYSTEM_GIT_REPO: &str = "https://github.com/MystenLabs/sui.git";
20
21#[derive(Debug)]
22pub struct SystemPackagesVersion {
23    pub git_revision: String,
24    pub packages: Vec<SystemPackage>,
25}
26
27#[derive(Debug)]
28pub struct SystemPackage {
29    /// The name of the package, e.g. "Sui"
30    pub package_name: String,
31
32    /// The path to the package in the sui monorepo
33    /// e.g. "crates/sui-framework/packages/sui-framework"
34    pub repo_path: String,
35}
36
37impl PartialEq for SystemPackagesVersion {
38    fn eq(&self, other: &Self) -> bool {
39        self.git_revision == other.git_revision
40    }
41}
42
43/// Return the system packages snapshot for the latest known protocol version
44pub fn latest_system_packages() -> &'static SystemPackagesVersion {
45    VERSION_TABLE
46        .last_key_value()
47        .expect("known system package version table should be nonempty")
48        .1
49}
50
51/// Return the latest protocol version that is not newer than the requested `version`
52/// (or `Err` if there is no such version).
53///
54/// The returned [ProtocolVersion] is the protocol version that introduced the returned
55/// [SystemPackagesVersion]; this may be older than the requested `version` if either:
56/// 1. the system packages did not change when `version` was released, or
57/// 2. this binary is older than the requested version and therefore doesn't know about the latest
58///    version of the system packages
59///
60/// You can distinguish these cases by comparing `version` with [ProtocolVersion::MAX].
61pub fn system_packages_for_protocol(
62    version: ProtocolVersion,
63) -> anyhow::Result<(&'static SystemPackagesVersion, ProtocolVersion)> {
64    let (protocol, system_packages) = VERSION_TABLE
65        .range(..=version)
66        .next_back()
67        .context(format!("Unrecognized protocol version {version:?}"))?;
68    Ok((system_packages, *protocol))
69}
70
71#[test]
72/// There is at least one known version of the system packages
73fn test_nonempty_version_table() {
74    assert!(!VERSION_TABLE.is_empty());
75}
76
77#[test]
78/// the hash for a specific version that we have one for is correctly returned
79fn test_exact_version() {
80    let (system_packages, protocol) = system_packages_for_protocol(4.into()).unwrap();
81    assert_eq!(
82        system_packages.git_revision,
83        "f5d26f1b3ae89f68cb66f3a007e90065e5286905"
84    );
85    assert_eq!(protocol, 4.into());
86    assert!(
87        system_packages
88            .packages
89            .iter()
90            .any(|p| p.package_name == "Sui")
91    );
92}
93
94#[test]
95/// we get the right hash for a version that we don't have an exact entry for
96fn test_gap_version() {
97    // versions 56 and 57 are missing in the manifest; version 55 should be returned
98    assert_eq!(
99        system_packages_for_protocol(56.into()).unwrap(),
100        system_packages_for_protocol(55.into()).unwrap(),
101    );
102    assert_eq!(
103        system_packages_for_protocol(57.into()).unwrap(),
104        system_packages_for_protocol(55.into()).unwrap(),
105    );
106    // version 58 is present though!
107    assert_ne!(
108        system_packages_for_protocol(58.into()).unwrap(),
109        system_packages_for_protocol(55.into()).unwrap(),
110    );
111}
112
113#[test]
114/// we get the correct hash for the latest known protocol version
115fn test_version_latest() {
116    assert_eq!(
117        system_packages_for_protocol(ProtocolVersion::MAX)
118            .unwrap()
119            .0,
120        latest_system_packages()
121    );
122    assert_eq!(
123        system_packages_for_protocol(ProtocolVersion::MAX + 1)
124            .unwrap()
125            .0,
126        latest_system_packages()
127    );
128}
129
130#[test]
131/// we get an error if the protocol version is too small or too large
132fn test_version_errors() {
133    assert!(system_packages_for_protocol(0.into()).is_err());
134}