sui_adapter_latest/static_programmable_transactions/linkage/
resolution.rs1use crate::{
5 data_store::PackageStore, static_programmable_transactions::linkage::config::ResolutionConfig,
6};
7use move_vm_runtime::validation::verification::ast::Package as VerifiedPackage;
8use std::{
9 borrow::Borrow,
10 collections::{BTreeMap, btree_map::Entry},
11 sync::Arc,
12};
13use sui_types::{
14 base_types::ObjectID, error::ExecutionError, execution_status::ExecutionErrorKind,
15};
16
17#[derive(Debug, Clone)]
19pub enum VersionConstraint {
20 Exact(u64, ObjectID),
24 AtLeast(u64, ObjectID),
28}
29
30#[derive(Debug, Clone)]
31pub(crate) struct ResolutionTable {
32 pub(crate) config: ResolutionConfig,
33 pub(crate) resolution_table: BTreeMap<ObjectID, VersionConstraint>,
34 pub(crate) all_versions_resolution_table: BTreeMap<ObjectID, ObjectID>,
37}
38
39impl ResolutionTable {
40 pub fn empty(config: ResolutionConfig) -> Self {
41 Self {
42 config,
43 resolution_table: BTreeMap::new(),
44 all_versions_resolution_table: BTreeMap::new(),
45 }
46 }
47
48 pub fn add_type_linkages_to_table<I>(
52 &mut self,
53 ids: I,
54 store: &dyn PackageStore,
55 ) -> Result<(), ExecutionError>
56 where
57 I: IntoIterator,
58 I::Item: Borrow<ObjectID>,
59 {
60 for id in ids {
61 let pkg = get_package(id.borrow(), store)?;
62 let transitive_deps = self
63 .config
64 .linkage_table(&pkg)
65 .into_values()
66 .map(ObjectID::from);
67 let package_id = pkg.version_id().into();
68 add_and_unify(&package_id, store, self, VersionConstraint::at_least)?;
69 for object_id in transitive_deps {
70 add_and_unify(&object_id, store, self, VersionConstraint::at_least)?;
71 }
72 }
73 Ok(())
74 }
75}
76
77impl VersionConstraint {
78 pub fn exact(pkg: &VerifiedPackage) -> Option<VersionConstraint> {
79 Some(VersionConstraint::Exact(
80 pkg.version(),
81 pkg.version_id().into(),
82 ))
83 }
84
85 pub fn at_least(pkg: &VerifiedPackage) -> Option<VersionConstraint> {
86 Some(VersionConstraint::AtLeast(
87 pkg.version(),
88 pkg.version_id().into(),
89 ))
90 }
91
92 pub fn unify(&self, other: &VersionConstraint) -> Result<VersionConstraint, ExecutionError> {
93 match (&self, other) {
94 (VersionConstraint::Exact(sv, self_id), VersionConstraint::Exact(ov, other_id)) => {
96 if self_id != other_id || sv != ov {
97 Err(ExecutionError::new_with_source(
98 ExecutionErrorKind::InvalidLinkage,
99 format!(
100 "exact/exact conflicting resolutions for package: linkage requires the same package \
101 at different versions. Linkage requires exactly {self_id} (version {sv}) and \
102 {other_id} (version {ov}) to be used in the same transaction"
103 ),
104 ))
105 } else {
106 Ok(VersionConstraint::Exact(*sv, *self_id))
107 }
108 }
109 (
111 VersionConstraint::AtLeast(self_version, sid),
112 VersionConstraint::AtLeast(other_version, oid),
113 ) => {
114 let id = if self_version > other_version {
115 *sid
116 } else {
117 *oid
118 };
119
120 Ok(VersionConstraint::AtLeast(
121 *self_version.max(other_version),
122 id,
123 ))
124 }
125 (
128 VersionConstraint::Exact(exact_version, exact_id),
129 VersionConstraint::AtLeast(at_least_version, at_least_id),
130 )
131 | (
132 VersionConstraint::AtLeast(at_least_version, at_least_id),
133 VersionConstraint::Exact(exact_version, exact_id),
134 ) => {
135 if exact_version < at_least_version {
136 return Err(ExecutionError::new_with_source(
137 ExecutionErrorKind::InvalidLinkage,
138 format!(
139 "Exact/AtLeast conflicting resolutions for package: linkage requires exactly this \
140 package {exact_id} (version {exact_version}) and also at least the following \
141 version of the package {at_least_id} at version {at_least_version}. However \
142 {exact_id} is at version {exact_version} which is less than {at_least_version}."
143 ),
144 ));
145 }
146
147 Ok(VersionConstraint::Exact(*exact_version, *exact_id))
148 }
149 }
150 }
151}
152
153pub(crate) fn get_package(
156 object_id: &ObjectID,
157 store: &dyn PackageStore,
158) -> Result<Arc<VerifiedPackage>, ExecutionError> {
159 store
160 .get_package(object_id)
161 .map_err(|e| {
162 ExecutionError::new_with_source(ExecutionErrorKind::PublishUpgradeMissingDependency, e)
163 })?
164 .ok_or_else(|| ExecutionError::from_kind(ExecutionErrorKind::InvalidLinkage))
165}
166
167pub(crate) fn add_and_unify(
170 object_id: &ObjectID,
171 store: &dyn PackageStore,
172 resolution_table: &mut ResolutionTable,
173 resolution_fn: fn(&VerifiedPackage) -> Option<VersionConstraint>,
174) -> Result<(), ExecutionError> {
175 let package = get_package(object_id, store)?;
176
177 let Some(resolution) = resolution_fn(&package) else {
178 return Ok(());
181 };
182 let original_pkg_id = package.original_id().into();
183
184 if let Entry::Vacant(e) = resolution_table.resolution_table.entry(original_pkg_id) {
185 e.insert(resolution);
186 } else {
187 let existing_unifier = resolution_table
188 .resolution_table
189 .get_mut(&original_pkg_id)
190 .expect("Guaranteed to exist");
191 *existing_unifier = existing_unifier.unify(&resolution)?;
192 }
193
194 if !resolution_table
195 .all_versions_resolution_table
196 .contains_key(object_id)
197 {
198 resolution_table
199 .all_versions_resolution_table
200 .insert(*object_id, original_pkg_id);
201 }
202
203 Ok(())
204}