sui_framework/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use move_binary_format::normalized;
5use move_binary_format::{
6    CompiledModule, binary_config::BinaryConfig, compatibility::Compatibility,
7};
8use move_core_types::gas_algebra::InternalGas;
9use serde::{Deserialize, Serialize};
10use std::fmt::Formatter;
11use std::sync::LazyLock;
12use sui_types::base_types::ObjectRef;
13use sui_types::storage::ObjectStore;
14use sui_types::{BRIDGE_PACKAGE_ID, DEEPBOOK_PACKAGE_ID};
15use sui_types::{
16    MOVE_STDLIB_PACKAGE_ID, SUI_FRAMEWORK_PACKAGE_ID, SUI_SYSTEM_PACKAGE_ID,
17    base_types::ObjectID,
18    digests::TransactionDigest,
19    move_package::MovePackage,
20    object::{OBJECT_START_VERSION, Object},
21};
22use tracing::error;
23
24/// Encapsulates a system package in the framework
25pub struct SystemPackageMetadata {
26    /// The name of the package (e.g. "MoveStdLib")
27    pub name: String,
28    /// The path within the repo to the source (e.g. "crates/sui-framework/packages/move-stdlib")
29    pub path: String,
30    /// The compiled bytecode and object ID of the package
31    pub compiled: SystemPackage,
32}
33
34/// Encapsulates the chain-relevant data about a framework package (such as the id or compiled
35/// bytecode)
36#[derive(Clone, Serialize, PartialEq, Eq, Deserialize)]
37pub struct SystemPackage {
38    pub id: ObjectID,
39    pub bytes: Vec<Vec<u8>>,
40    pub dependencies: Vec<ObjectID>,
41}
42
43impl SystemPackageMetadata {
44    pub fn new(
45        name: impl ToString,
46        path: impl ToString,
47        id: ObjectID,
48        raw_bytes: &'static [u8],
49        dependencies: &[ObjectID],
50    ) -> Self {
51        SystemPackageMetadata {
52            name: name.to_string(),
53            path: path.to_string(),
54            compiled: SystemPackage::new(id, raw_bytes, dependencies),
55        }
56    }
57}
58
59impl SystemPackage {
60    pub fn new(id: ObjectID, raw_bytes: &'static [u8], dependencies: &[ObjectID]) -> Self {
61        let bytes: Vec<Vec<u8>> = bcs::from_bytes(raw_bytes).unwrap();
62        Self {
63            id,
64            bytes,
65            dependencies: dependencies.to_vec(),
66        }
67    }
68
69    pub fn modules(&self) -> Vec<CompiledModule> {
70        self.bytes
71            .iter()
72            .map(|b| CompiledModule::deserialize_with_defaults(b).unwrap())
73            .collect()
74    }
75
76    pub fn genesis_move_package(&self) -> MovePackage {
77        MovePackage::new_system(
78            OBJECT_START_VERSION,
79            &self.modules(),
80            self.dependencies.iter().copied(),
81        )
82    }
83
84    pub fn genesis_object(&self) -> Object {
85        Object::new_system_package(
86            &self.modules(),
87            OBJECT_START_VERSION,
88            self.dependencies.to_vec(),
89            TransactionDigest::genesis_marker(),
90        )
91    }
92}
93
94impl std::fmt::Debug for SystemPackage {
95    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
96        writeln!(f, "Object ID: {:?}", self.id)?;
97        writeln!(f, "Size: {}", self.bytes.len())?;
98        writeln!(f, "Dependencies: {:?}", self.dependencies)?;
99        Ok(())
100    }
101}
102
103macro_rules! define_system_package_metadata {
104    ([$(($id:expr, $name: expr, $path:expr, $deps:expr)),* $(,)?]) => {{
105        static PACKAGES: LazyLock<Vec<SystemPackageMetadata>> = LazyLock::new(|| {
106            vec![
107                $(SystemPackageMetadata::new(
108                    $name,
109                    concat!("crates/sui-framework/packages/", $path),
110                    $id,
111                    include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/packages_compiled", "/", $path)),
112                    &$deps,
113                )),*
114            ]
115        });
116        &PACKAGES
117    }}
118}
119
120pub struct BuiltInFramework;
121impl BuiltInFramework {
122    pub fn iter_system_package_metadata() -> impl Iterator<Item = &'static SystemPackageMetadata> {
123        // All system packages in the current build should be registered here, and this is the only
124        // place we need to worry about if any of them changes.
125        // TODO: Is it possible to derive dependencies from the bytecode instead of manually specifying them?
126        define_system_package_metadata!([
127            (MOVE_STDLIB_PACKAGE_ID, "MoveStdlib", "move-stdlib", []),
128            (
129                SUI_FRAMEWORK_PACKAGE_ID,
130                "Sui",
131                "sui-framework",
132                [MOVE_STDLIB_PACKAGE_ID]
133            ),
134            (
135                SUI_SYSTEM_PACKAGE_ID,
136                "SuiSystem",
137                "sui-system",
138                [MOVE_STDLIB_PACKAGE_ID, SUI_FRAMEWORK_PACKAGE_ID]
139            ),
140            (
141                DEEPBOOK_PACKAGE_ID,
142                "DeepBook",
143                "deepbook",
144                [MOVE_STDLIB_PACKAGE_ID, SUI_FRAMEWORK_PACKAGE_ID]
145            ),
146            (
147                BRIDGE_PACKAGE_ID,
148                "Bridge",
149                "bridge",
150                [
151                    MOVE_STDLIB_PACKAGE_ID,
152                    SUI_FRAMEWORK_PACKAGE_ID,
153                    SUI_SYSTEM_PACKAGE_ID
154                ]
155            )
156        ])
157        .iter()
158    }
159
160    pub fn all_package_ids() -> Vec<ObjectID> {
161        Self::iter_system_packages().map(|p| p.id).collect()
162    }
163
164    pub fn get_package_by_id(id: &ObjectID) -> &'static SystemPackage {
165        Self::iter_system_packages().find(|s| &s.id == id).unwrap()
166    }
167
168    pub fn iter_system_packages() -> impl Iterator<Item = &'static SystemPackage> {
169        BuiltInFramework::iter_system_package_metadata().map(|m| &m.compiled)
170    }
171
172    pub fn genesis_move_packages() -> impl Iterator<Item = MovePackage> {
173        Self::iter_system_packages().map(|package| package.genesis_move_package())
174    }
175
176    pub fn genesis_objects() -> impl Iterator<Item = Object> {
177        Self::iter_system_packages().map(|package| package.genesis_object())
178    }
179}
180
181pub const DEFAULT_FRAMEWORK_PATH: &str = env!("CARGO_MANIFEST_DIR");
182
183pub fn legacy_test_cost() -> InternalGas {
184    InternalGas::new(0)
185}
186
187/// Check whether the framework defined by `modules` is compatible with the framework that is
188/// already on-chain (i.e. stored in `object_store`) at `id`.
189///
190/// - Returns `None` if the current package at `id` cannot be loaded, or the compatibility check
191///   fails (This is grounds not to upgrade).
192/// - Panics if the object at `id` can be loaded but is not a package -- this is an invariant
193///   violation.
194/// - Returns the digest of the current framework (and version) if it is equivalent to the new
195///   framework (indicates support for a protocol upgrade without a framework upgrade).
196/// - Returns the digest of the new framework (and version) if it is compatible (indicates
197///   support for a protocol upgrade with a framework upgrade).
198pub async fn compare_system_package<S: ObjectStore>(
199    object_store: &S,
200    id: &ObjectID,
201    modules: &[CompiledModule],
202    dependencies: Vec<ObjectID>,
203    binary_config: &BinaryConfig,
204) -> Option<ObjectRef> {
205    let cur_object = match object_store.get_object(id) {
206        Some(cur_object) => cur_object,
207
208        None => {
209            // creating a new framework package--nothing to check
210            return Some(
211                Object::new_system_package(
212                    modules,
213                    // note: execution_engine assumes any system package with version OBJECT_START_VERSION is freshly created
214                    // rather than upgraded
215                    OBJECT_START_VERSION,
216                    dependencies,
217                    // Genesis is fine here, we only use it to calculate an object ref that we can use
218                    // for all validators to commit to the same bytes in the update
219                    TransactionDigest::genesis_marker(),
220                )
221                .compute_object_reference(),
222            );
223        }
224    };
225
226    let cur_ref = cur_object.compute_object_reference();
227    let cur_pkg = cur_object
228        .data
229        .try_as_package()
230        .expect("Framework not package");
231
232    let mut new_object = Object::new_system_package(
233        modules,
234        // Start at the same version as the current package, and increment if compatibility is
235        // successful
236        cur_object.version(),
237        dependencies,
238        cur_object.previous_transaction,
239    );
240
241    if cur_ref == new_object.compute_object_reference() {
242        return Some(cur_ref);
243    }
244
245    let compatibility = Compatibility::framework_upgrade_check();
246
247    let new_pkg = new_object
248        .data
249        .try_as_package_mut()
250        .expect("Created as package");
251
252    let pool = &mut normalized::RcPool::new();
253    let cur_normalized = match cur_pkg.normalize(pool, binary_config, /* include code */ false) {
254        Ok(v) => v,
255        Err(e) => {
256            error!("Could not normalize existing package: {e:?}");
257            return None;
258        }
259    };
260    let mut new_normalized = new_pkg
261        .normalize(pool, binary_config, /* include code */ false)
262        .ok()?;
263
264    for (name, cur_module) in cur_normalized {
265        let new_module = new_normalized.remove(&name)?;
266
267        if let Err(e) = compatibility.check(&cur_module, &new_module) {
268            error!("Compatibility check failed, for new version of {id}::{name}: {e:?}");
269            return None;
270        }
271    }
272
273    new_pkg.increment_version();
274    Some(new_object.compute_object_reference())
275}