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