snarkvm_synthesizer_process/stack/helpers/
check_upgrade.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use super::*;
17
18impl<N: Network> Stack<N> {
19    /// Checks that the new program is a valid upgrade.
20    /// At a high-level, an upgrade must preserve the existing interfaces of the original program.
21    /// An upgrade may add new components, except for constructors, and modify logic **only** in functions and finalize scopes.
22    /// An upgrade may also be exactly the same as the original program.
23    ///
24    /// The order of the components in the new program may be modified, as long as the interfaces remain the same.
25    ///
26    /// An detailed overview of what an upgrade can and cannot do is given below:
27    ///  | Program Component | Delete |    Modify    |  Add  |
28    ///  |-------------------|--------|--------------|-------|
29    ///  | import            |   ❌   |      ❌      |  ✅   |
30    ///  | constructor       |   ❌   |      ❌      |  ❌   |
31    ///  | mapping           |   ❌   |      ❌      |  ✅   |
32    ///  | struct            |   ❌   |      ❌      |  ✅   |
33    ///  | record            |   ❌   |      ❌      |  ✅   |
34    ///  | closure           |   ❌   |      ❌      |  ✅   |
35    ///  | function          |   ❌   | ✅ (logic)   |  ✅   |
36    ///  | finalize          |   ❌   | ✅ (logic)   |  ✅   |
37    ///  |-------------------|--------|--------------|-------|
38    ///
39    #[inline]
40    pub fn check_upgrade_is_valid(old_program: &Program<N>, new_program: &Program<N>) -> Result<()> {
41        // Get the new program ID.
42        let program_id = new_program.id();
43        // Ensure the program is not `credits.aleo`.
44        ensure!(program_id != &ProgramID::from_str("credits.aleo")?, "Cannot upgrade 'credits.aleo'");
45        // Ensure the program ID matches.
46        ensure!(old_program.id() == new_program.id(), "Cannot upgrade '{program_id}' with different program ID");
47        // Ensure that all of the imports in the old program exist in the new program.
48        for old_import in old_program.imports().keys() {
49            if !new_program.contains_import(old_import) {
50                bail!("Cannot upgrade '{program_id}' because it is missing the original import '{old_import}'");
51            }
52        }
53        // Ensure that the constructors in both programs are exactly the same.
54        // Note: Programs without constructors are not allowed to be upgraded.
55        match (old_program.constructor(), new_program.constructor()) {
56            (_, None) => bail!("A program cannot be upgraded to a program without a constructor"),
57            (None, _) => bail!("A program without a constructor cannot be upgraded"),
58            (Some(old_constructor), Some(new_constructor)) => {
59                ensure!(
60                    old_constructor == new_constructor,
61                    "Cannot upgrade '{program_id}' because the constructor does not match"
62                );
63            }
64        }
65        // Ensure that all of the mappings in the old program exist in the new program.
66        for (old_mapping_id, old_mapping_type) in old_program.mappings() {
67            let new_mapping_type = new_program.get_mapping(old_mapping_id)?;
68            ensure!(
69                *old_mapping_type == new_mapping_type,
70                "Cannot upgrade '{program_id}' because the mapping '{old_mapping_id}' does not match"
71            );
72        }
73        // Ensure that all of the structs in the old program exist in the new program.
74        for (old_struct_id, old_struct_type) in old_program.structs() {
75            let new_struct_type = new_program.get_struct(old_struct_id)?;
76            ensure!(
77                old_struct_type == new_struct_type,
78                "Cannot upgrade '{program_id}' because the struct '{old_struct_id}' does not match"
79            );
80        }
81        // Ensure that all of the records in the old program exist in the new program.
82        for (old_record_id, old_record_type) in old_program.records() {
83            let new_record_type = new_program.get_record(old_record_id)?;
84            ensure!(
85                old_record_type == new_record_type,
86                "Cannot upgrade '{program_id}' because the record '{old_record_id}' does not match"
87            );
88        }
89        // Ensure that the old program closures exist in the new program, with the exact same definition.
90        for old_closure in old_program.closures().values() {
91            let old_closure_name = old_closure.name();
92            let new_closure = new_program.get_closure(old_closure_name)?;
93            ensure!(
94                old_closure == &new_closure,
95                "Cannot upgrade '{program_id}' because the closure '{old_closure_name}' does not match"
96            );
97        }
98        // Ensure that the old program functions exist in the new program, with the same input and output types.
99        // If the function has an associated `finalize` block, then ensure that the finalize block exists in the new program.
100        for old_function in old_program.functions().values() {
101            let old_function_name = old_function.name();
102            let new_function = new_program.get_function_ref(old_function_name)?;
103            ensure!(
104                old_function.input_types() == new_function.input_types(),
105                "Cannot upgrade '{program_id}' because the input types to the function '{old_function_name}' do not match"
106            );
107            ensure!(
108                old_function.output_types() == new_function.output_types(),
109                "Cannot upgrade '{program_id}' because the output types of the function '{old_function_name}' do not match"
110            );
111            match (old_function.finalize_logic(), new_function.finalize_logic()) {
112                (None, None) => {} // Do nothing
113                (None, Some(_)) => bail!(
114                    "Cannot upgrade '{program_id}' because the function '{old_function_name}' should not have a finalize block"
115                ),
116                (Some(_), None) => bail!(
117                    "Cannot upgrade '{program_id}' because the function '{old_function_name}' should have a finalize block"
118                ),
119                (Some(old_finalize), Some(new_finalize)) => {
120                    ensure!(
121                        old_finalize.input_types() == new_finalize.input_types(),
122                        "Cannot upgrade '{program_id}' because the finalize inputs to the function '{old_function_name}' do not match"
123                    );
124                }
125            }
126        }
127        Ok(())
128    }
129}