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