sui_adapter_v0/programmable_transactions/
linkage_view.rs1use std::{
5 cell::RefCell,
6 collections::{hash_map::Entry, BTreeMap, HashMap, HashSet},
7 str::FromStr,
8};
9
10use crate::execution_value::SuiResolver;
11use move_core_types::{
12 account_address::AccountAddress,
13 identifier::{IdentStr, Identifier},
14 language_storage::ModuleId,
15 resolver::{ModuleResolver, SerializedPackage},
16};
17use move_vm_types::data_store::LinkageResolver;
18use sui_types::storage::{get_module, PackageObject};
19use sui_types::{
20 base_types::ObjectID,
21 error::{ExecutionError, SuiError, SuiResult},
22 move_package::{MovePackage, TypeOrigin, UpgradeInfo},
23 storage::BackingPackageStore,
24};
25
26pub struct LinkageView<'state> {
29 resolver: Box<dyn SuiResolver + 'state>,
31 linkage_info: LinkageInfo,
33 type_origin_cache: RefCell<HashMap<ModuleId, HashMap<Identifier, AccountAddress>>>,
41 past_contexts: RefCell<HashSet<ObjectID>>,
44}
45
46#[derive(Debug)]
47pub enum LinkageInfo {
48 Unset,
50 Universal,
52 Set(PackageLinkage),
55}
56
57#[derive(Debug)]
58pub struct PackageLinkage {
59 storage_id: AccountAddress,
60 runtime_id: AccountAddress,
61 link_table: BTreeMap<ObjectID, UpgradeInfo>,
62}
63
64pub struct SavedLinkage(PackageLinkage);
65
66impl<'state> LinkageView<'state> {
67 pub fn new(resolver: Box<dyn SuiResolver + 'state>, linkage_info: LinkageInfo) -> Self {
68 Self {
69 resolver,
70 linkage_info,
71 type_origin_cache: RefCell::new(HashMap::new()),
72 past_contexts: RefCell::new(HashSet::new()),
73 }
74 }
75
76 pub fn reset_linkage(&mut self) {
77 if let LinkageInfo::Set(_) = &self.linkage_info {
78 self.linkage_info = LinkageInfo::Unset;
80 }
81 }
82
83 pub fn has_linkage(&self, context: ObjectID) -> bool {
86 match &self.linkage_info {
87 LinkageInfo::Unset => false,
88 LinkageInfo::Universal => true,
89 LinkageInfo::Set(linkage) => linkage.storage_id == *context,
90 }
91 }
92
93 pub fn steal_linkage(&mut self) -> Option<SavedLinkage> {
95 if let LinkageInfo::Universal = &self.linkage_info {
96 None
97 } else {
98 match std::mem::replace(&mut self.linkage_info, LinkageInfo::Unset) {
99 LinkageInfo::Set(linkage) => Some(SavedLinkage(linkage)),
100 LinkageInfo::Unset => None,
101 LinkageInfo::Universal => unreachable!(),
102 }
103 }
104 }
105
106 pub fn restore_linkage(&mut self, saved: Option<SavedLinkage>) -> Result<(), ExecutionError> {
108 let Some(SavedLinkage(saved)) = saved else {
109 return Ok(());
110 };
111
112 match &self.linkage_info {
113 LinkageInfo::Unset => (),
114 LinkageInfo::Universal => (),
115 LinkageInfo::Set(existing) => {
116 invariant_violation!(
117 "Attempt to overwrite linkage by restoring: {saved:#?} \
118 Existing linkage: {existing:#?}",
119 )
120 }
121 }
122
123 self.linkage_info = LinkageInfo::Set(saved);
126 Ok(())
127 }
128
129 pub fn set_linkage(&mut self, context: &MovePackage) -> Result<AccountAddress, ExecutionError> {
133 match &self.linkage_info {
134 LinkageInfo::Unset => (),
135 LinkageInfo::Universal => return Ok(*context.id()),
136
137 LinkageInfo::Set(existing) => {
138 invariant_violation!(
139 "Attempt to overwrite linkage info with context from {}. \
140 Existing linkage: {existing:#?}",
141 context.id(),
142 )
143 }
144 }
145
146 let linkage = PackageLinkage::from(context);
147 let storage_id = context.id();
148 let runtime_id = linkage.runtime_id;
149 self.linkage_info = LinkageInfo::Set(linkage);
150
151 if !self.past_contexts.borrow_mut().insert(storage_id) {
152 return Ok(runtime_id);
153 }
154
155 for TypeOrigin {
159 module_name,
160 datatype_name: struct_name,
161 package: defining_id,
162 } in context.type_origin_table()
163 {
164 let Ok(module_name) = Identifier::from_str(module_name) else {
165 invariant_violation!("Module name isn't an identifier: {module_name}");
166 };
167
168 let Ok(struct_name) = Identifier::from_str(struct_name) else {
169 invariant_violation!("Struct name isn't an identifier: {struct_name}");
170 };
171
172 let runtime_id = ModuleId::new(runtime_id, module_name);
173 self.add_type_origin(runtime_id, struct_name, *defining_id)?;
174 }
175
176 Ok(runtime_id)
177 }
178
179 pub fn original_package_id(&self) -> Option<AccountAddress> {
180 if let LinkageInfo::Set(linkage) = &self.linkage_info {
181 Some(linkage.runtime_id)
182 } else {
183 None
184 }
185 }
186
187 fn get_cached_type_origin(
188 &self,
189 runtime_id: &ModuleId,
190 struct_: &IdentStr,
191 ) -> Option<AccountAddress> {
192 self.type_origin_cache
193 .borrow()
194 .get(runtime_id)?
195 .get(struct_)
196 .cloned()
197 }
198
199 fn add_type_origin(
200 &self,
201 runtime_id: ModuleId,
202 struct_: Identifier,
203 defining_id: ObjectID,
204 ) -> Result<(), ExecutionError> {
205 let mut cache = self.type_origin_cache.borrow_mut();
206 let module_cache = cache.entry(runtime_id.clone()).or_default();
207
208 match module_cache.entry(struct_) {
209 Entry::Vacant(entry) => {
210 entry.insert(*defining_id);
211 }
212
213 Entry::Occupied(entry) => {
214 if entry.get() != &*defining_id {
215 invariant_violation!(
216 "Conflicting defining ID for {}::{}: {} and {}",
217 runtime_id,
218 entry.key(),
219 defining_id,
220 entry.get(),
221 );
222 }
223 }
224 }
225
226 Ok(())
227 }
228}
229
230impl From<&MovePackage> for PackageLinkage {
231 fn from(package: &MovePackage) -> Self {
232 Self {
233 storage_id: package.id().into(),
234 runtime_id: package.original_package_id().into(),
235 link_table: package.linkage_table().clone(),
236 }
237 }
238}
239
240impl LinkageResolver for LinkageView<'_> {
241 type Error = SuiError;
242
243 fn link_context(&self) -> AccountAddress {
244 if let LinkageInfo::Set(linkage) = &self.linkage_info {
245 linkage.storage_id
246 } else {
247 AccountAddress::ZERO
248 }
249 }
250
251 fn relocate(&self, module_id: &ModuleId) -> Result<ModuleId, Self::Error> {
252 let linkage = match &self.linkage_info {
253 LinkageInfo::Set(linkage) => linkage,
254 LinkageInfo::Universal => return Ok(module_id.clone()),
255
256 LinkageInfo::Unset => {
257 invariant_violation!("No linkage context set while relocating {module_id}.")
258 }
259 };
260
261 if module_id.address() == &linkage.runtime_id {
264 return Ok(ModuleId::new(
265 linkage.storage_id,
266 module_id.name().to_owned(),
267 ));
268 }
269
270 let runtime_id = ObjectID::from_address(*module_id.address());
271 let Some(upgrade) = linkage.link_table.get(&runtime_id) else {
272 invariant_violation!(
273 "Missing linkage for {runtime_id} in context {}, runtime_id is {}",
274 linkage.storage_id,
275 linkage.runtime_id
276 );
277 };
278
279 Ok(ModuleId::new(
280 upgrade.upgraded_id.into(),
281 module_id.name().to_owned(),
282 ))
283 }
284
285 fn defining_module(
286 &self,
287 runtime_id: &ModuleId,
288 struct_: &IdentStr,
289 ) -> Result<ModuleId, Self::Error> {
290 match &self.linkage_info {
291 LinkageInfo::Set(_) => (),
292 LinkageInfo::Universal => return Ok(runtime_id.clone()),
293
294 LinkageInfo::Unset => {
295 invariant_violation!(
296 "No linkage context set for defining module query on {runtime_id}::{struct_}."
297 )
298 }
299 };
300
301 if let Some(cached) = self.get_cached_type_origin(runtime_id, struct_) {
302 return Ok(ModuleId::new(cached, runtime_id.name().to_owned()));
303 }
304
305 let storage_id = ObjectID::from(*self.relocate(runtime_id)?.address());
306 let Some(package) = self.resolver.get_package_object(&storage_id)? else {
307 invariant_violation!("Missing dependent package in store: {storage_id}",)
308 };
309
310 for TypeOrigin {
311 module_name,
312 datatype_name: struct_name,
313 package,
314 } in package.move_package().type_origin_table()
315 {
316 if module_name == runtime_id.name().as_str() && struct_name == struct_.as_str() {
317 self.add_type_origin(runtime_id.clone(), struct_.to_owned(), *package)?;
318 return Ok(ModuleId::new(**package, runtime_id.name().to_owned()));
319 }
320 }
321
322 invariant_violation!(
323 "{runtime_id}::{struct_} not found in type origin table in {storage_id} (v{})",
324 package.move_package().version(),
325 )
326 }
327}
328
329impl ModuleResolver for LinkageView<'_> {
332 type Error = SuiError;
333
334 fn get_module(&self, id: &ModuleId) -> Result<Option<Vec<u8>>, Self::Error> {
335 get_module(self, id)
336 }
337
338 fn get_packages_static<const N: usize>(
339 &self,
340 _ids: [AccountAddress; N],
341 ) -> Result<[Option<SerializedPackage>; N], Self::Error> {
342 unreachable!("v0 get_packages_static should not be called on LinkageView")
343 }
344
345 fn get_packages<'a>(
346 &self,
347 _ids: impl ExactSizeIterator<Item = &'a AccountAddress>,
348 ) -> Result<Vec<Option<SerializedPackage>>, Self::Error> {
349 unreachable!("v0 get_packages should not be called on LinkageView")
350 }
351}
352
353impl BackingPackageStore for LinkageView<'_> {
354 fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
355 self.resolver.get_package_object(package_id)
356 }
357}