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