sui_adapter_latest/static_programmable_transactions/linkage/
resolution.rs1use crate::data_store::PackageStore;
5use std::{
6 collections::{BTreeMap, btree_map::Entry},
7 rc::Rc,
8};
9use sui_types::{
10 base_types::{ObjectID, SequenceNumber},
11 error::{ExecutionError, ExecutionErrorKind},
12 move_package::MovePackage,
13};
14
15#[derive(Debug, Clone)]
17pub enum VersionConstraint {
18 Exact(SequenceNumber, ObjectID),
22 AtLeast(SequenceNumber, ObjectID),
26}
27
28#[derive(Debug, Clone)]
29pub(crate) struct ResolutionTable {
30 pub(crate) resolution_table: BTreeMap<ObjectID, VersionConstraint>,
31 pub(crate) all_versions_resolution_table: BTreeMap<ObjectID, ObjectID>,
34}
35
36impl ResolutionTable {
37 pub fn empty() -> Self {
38 Self {
39 resolution_table: BTreeMap::new(),
40 all_versions_resolution_table: BTreeMap::new(),
41 }
42 }
43}
44
45impl VersionConstraint {
46 pub fn exact(pkg: &MovePackage) -> Option<VersionConstraint> {
47 Some(VersionConstraint::Exact(pkg.version(), pkg.id()))
48 }
49
50 pub fn at_least(pkg: &MovePackage) -> Option<VersionConstraint> {
51 Some(VersionConstraint::AtLeast(pkg.version(), pkg.id()))
52 }
53
54 pub fn unify(&self, other: &VersionConstraint) -> Result<VersionConstraint, ExecutionError> {
55 match (&self, other) {
56 (VersionConstraint::Exact(sv, self_id), VersionConstraint::Exact(ov, other_id)) => {
58 if self_id != other_id || sv != ov {
59 Err(ExecutionError::new_with_source(
60 ExecutionErrorKind::InvalidLinkage,
61 format!(
62 "exact/exact conflicting resolutions for package: linkage requires the same package \
63 at different versions. Linkage requires exactly {self_id} (version {sv}) and \
64 {other_id} (version {ov}) to be used in the same transaction"
65 ),
66 ))
67 } else {
68 Ok(VersionConstraint::Exact(*sv, *self_id))
69 }
70 }
71 (
73 VersionConstraint::AtLeast(self_version, sid),
74 VersionConstraint::AtLeast(other_version, oid),
75 ) => {
76 let id = if self_version > other_version {
77 *sid
78 } else {
79 *oid
80 };
81
82 Ok(VersionConstraint::AtLeast(
83 *self_version.max(other_version),
84 id,
85 ))
86 }
87 (
90 VersionConstraint::Exact(exact_version, exact_id),
91 VersionConstraint::AtLeast(at_least_version, at_least_id),
92 )
93 | (
94 VersionConstraint::AtLeast(at_least_version, at_least_id),
95 VersionConstraint::Exact(exact_version, exact_id),
96 ) => {
97 if exact_version < at_least_version {
98 return Err(ExecutionError::new_with_source(
99 ExecutionErrorKind::InvalidLinkage,
100 format!(
101 "Exact/AtLeast conflicting resolutions for package: linkage requires exactly this \
102 package {exact_id} (version {exact_version}) and also at least the following \
103 version of the package {at_least_id} at version {at_least_version}. However \
104 {exact_id} is at version {exact_version} which is less than {at_least_version}."
105 ),
106 ));
107 }
108
109 Ok(VersionConstraint::Exact(*exact_version, *exact_id))
110 }
111 }
112 }
113}
114
115pub(crate) fn get_package(
118 object_id: &ObjectID,
119 store: &dyn PackageStore,
120) -> Result<Rc<MovePackage>, ExecutionError> {
121 store
122 .get_package(object_id)
123 .map_err(|e| {
124 ExecutionError::new_with_source(ExecutionErrorKind::PublishUpgradeMissingDependency, e)
125 })?
126 .ok_or_else(|| ExecutionError::from_kind(ExecutionErrorKind::InvalidLinkage))
127}
128
129pub(crate) fn add_and_unify(
132 object_id: &ObjectID,
133 store: &dyn PackageStore,
134 resolution_table: &mut ResolutionTable,
135 resolution_fn: fn(&MovePackage) -> Option<VersionConstraint>,
136) -> Result<(), ExecutionError> {
137 let package = get_package(object_id, store)?;
138
139 let Some(resolution) = resolution_fn(&package) else {
140 return Ok(());
143 };
144 let original_pkg_id = package.original_package_id();
145
146 if let Entry::Vacant(e) = resolution_table.resolution_table.entry(original_pkg_id) {
147 e.insert(resolution);
148 } else {
149 let existing_unifier = resolution_table
150 .resolution_table
151 .get_mut(&original_pkg_id)
152 .expect("Guaranteed to exist");
153 *existing_unifier = existing_unifier.unify(&resolution)?;
154 }
155
156 if !resolution_table
157 .all_versions_resolution_table
158 .contains_key(object_id)
159 {
160 resolution_table
161 .all_versions_resolution_table
162 .insert(*object_id, original_pkg_id);
163 }
164
165 Ok(())
166}