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::ExecutionErrorTrait, 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, E>(
52 &mut self,
53 ids: I,
54 store: &dyn PackageStore,
55 ) -> Result<(), E>
56 where
57 E: ExecutionErrorTrait,
58 I: IntoIterator,
59 I::Item: Borrow<ObjectID>,
60 {
61 for id in ids {
62 let pkg = get_package(id.borrow(), store)?;
63 let transitive_deps = self
64 .config
65 .linkage_table(&pkg)
66 .into_values()
67 .map(ObjectID::from);
68 let package_id = pkg.version_id().into();
69 add_and_unify(&package_id, store, self, VersionConstraint::at_least)?;
70 for object_id in transitive_deps {
71 add_and_unify(&object_id, store, self, VersionConstraint::at_least)?;
72 }
73 }
74 Ok(())
75 }
76}
77
78impl VersionConstraint {
79 pub fn exact(pkg: &VerifiedPackage) -> Option<VersionConstraint> {
80 Some(VersionConstraint::Exact(
81 pkg.version(),
82 pkg.version_id().into(),
83 ))
84 }
85
86 pub fn at_least(pkg: &VerifiedPackage) -> Option<VersionConstraint> {
87 Some(VersionConstraint::AtLeast(
88 pkg.version(),
89 pkg.version_id().into(),
90 ))
91 }
92
93 pub fn unify<E: ExecutionErrorTrait>(
94 &self,
95 other: &VersionConstraint,
96 ) -> Result<VersionConstraint, E> {
97 match (&self, other) {
98 (VersionConstraint::Exact(sv, self_id), VersionConstraint::Exact(ov, other_id)) => {
100 if self_id != other_id || sv != ov {
101 Err(E::new_with_source(
102 ExecutionErrorKind::InvalidLinkage,
103 format!(
104 "exact/exact conflicting resolutions for package: linkage requires the same package \
105 at different versions. Linkage requires exactly {self_id} (version {sv}) and \
106 {other_id} (version {ov}) to be used in the same transaction"
107 ),
108 ))
109 } else {
110 Ok(VersionConstraint::Exact(*sv, *self_id))
111 }
112 }
113 (
115 VersionConstraint::AtLeast(self_version, sid),
116 VersionConstraint::AtLeast(other_version, oid),
117 ) => {
118 let id = if self_version > other_version {
119 *sid
120 } else {
121 *oid
122 };
123
124 Ok(VersionConstraint::AtLeast(
125 *self_version.max(other_version),
126 id,
127 ))
128 }
129 (
132 VersionConstraint::Exact(exact_version, exact_id),
133 VersionConstraint::AtLeast(at_least_version, at_least_id),
134 )
135 | (
136 VersionConstraint::AtLeast(at_least_version, at_least_id),
137 VersionConstraint::Exact(exact_version, exact_id),
138 ) => {
139 if exact_version < at_least_version {
140 return Err(E::new_with_source(
141 ExecutionErrorKind::InvalidLinkage,
142 format!(
143 "Exact/AtLeast conflicting resolutions for package: linkage requires exactly this \
144 package {exact_id} (version {exact_version}) and also at least the following \
145 version of the package {at_least_id} at version {at_least_version}. However \
146 {exact_id} is at version {exact_version} which is less than {at_least_version}."
147 ),
148 ));
149 }
150
151 Ok(VersionConstraint::Exact(*exact_version, *exact_id))
152 }
153 }
154 }
155}
156
157pub(crate) fn get_package<E: ExecutionErrorTrait>(
160 object_id: &ObjectID,
161 store: &dyn PackageStore,
162) -> Result<Arc<VerifiedPackage>, E> {
163 store
164 .get_package(object_id)
165 .map_err(|e| E::new_with_source(ExecutionErrorKind::PublishUpgradeMissingDependency, e))?
166 .ok_or_else(|| E::from_kind(ExecutionErrorKind::InvalidLinkage))
167}
168
169pub(crate) fn add_and_unify<E: ExecutionErrorTrait>(
172 object_id: &ObjectID,
173 store: &dyn PackageStore,
174 resolution_table: &mut ResolutionTable,
175 resolution_fn: fn(&VerifiedPackage) -> Option<VersionConstraint>,
176) -> Result<(), E> {
177 let package = get_package(object_id, store)?;
178
179 let Some(resolution) = resolution_fn(&package) else {
180 return Ok(());
183 };
184 let original_pkg_id = package.original_id().into();
185
186 if let Entry::Vacant(e) = resolution_table.resolution_table.entry(original_pkg_id) {
187 e.insert(resolution);
188 } else {
189 let existing_unifier = resolution_table
190 .resolution_table
191 .get_mut(&original_pkg_id)
192 .expect("Guaranteed to exist");
193 *existing_unifier = existing_unifier.unify(&resolution)?;
194 }
195
196 if !resolution_table
197 .all_versions_resolution_table
198 .contains_key(object_id)
199 {
200 resolution_table
201 .all_versions_resolution_table
202 .insert(*object_id, original_pkg_id);
203 }
204
205 Ok(())
206}