iota_adapter_latest/programmable_transactions/
linkage_view.rs1use std::{
6 cell::RefCell,
7 collections::{BTreeMap, HashMap, HashSet, hash_map::Entry},
8 str::FromStr,
9};
10
11use iota_types::{
12 base_types::ObjectID,
13 error::{ExecutionError, IotaError, IotaResult},
14 move_package::{MovePackage, TypeOrigin, UpgradeInfo},
15 storage::{BackingPackageStore, PackageObject, get_module},
16};
17use move_core_types::{
18 account_address::AccountAddress,
19 identifier::{IdentStr, Identifier},
20 language_storage::{ModuleId, StructTag},
21 resolver::{LinkageResolver, ModuleResolver, ResourceResolver},
22};
23
24use crate::execution_value::IotaResolver;
25
26pub struct LinkageView<'state> {
30 resolver: Box<dyn IotaResolver + 'state>,
33 linkage_info: Option<LinkageInfo>,
35 type_origin_cache: RefCell<HashMap<ModuleId, HashMap<Identifier, AccountAddress>>>,
45 past_contexts: RefCell<HashSet<ObjectID>>,
49}
50
51#[derive(Debug)]
52pub struct LinkageInfo {
53 storage_id: AccountAddress,
54 runtime_id: AccountAddress,
55 link_table: BTreeMap<ObjectID, UpgradeInfo>,
56}
57
58pub struct SavedLinkage(LinkageInfo);
59
60impl<'state> LinkageView<'state> {
61 pub fn new(resolver: Box<dyn IotaResolver + 'state>) -> Self {
66 Self {
67 resolver,
68 linkage_info: None,
69 type_origin_cache: RefCell::new(HashMap::new()),
70 past_contexts: RefCell::new(HashSet::new()),
71 }
72 }
73
74 pub fn reset_linkage(&mut self) {
76 self.linkage_info = None;
77 }
78
79 pub fn has_linkage(&self, context: ObjectID) -> bool {
82 self.linkage_info
83 .as_ref()
84 .is_some_and(|l| l.storage_id.as_ref() == context.as_bytes())
85 }
86
87 pub fn steal_linkage(&mut self) -> Option<SavedLinkage> {
90 Some(SavedLinkage(self.linkage_info.take()?))
91 }
92
93 pub fn restore_linkage(&mut self, saved: Option<SavedLinkage>) -> Result<(), ExecutionError> {
96 let Some(SavedLinkage(saved)) = saved else {
97 return Ok(());
98 };
99
100 if let Some(existing) = &self.linkage_info {
101 invariant_violation!(
102 "Attempt to overwrite linkage by restoring: {saved:#?} \
103 Existing linkage: {existing:#?}",
104 )
105 }
106
107 self.linkage_info = Some(saved);
111 Ok(())
112 }
113
114 pub fn set_linkage(&mut self, context: &MovePackage) -> Result<AccountAddress, ExecutionError> {
118 if let Some(existing) = &self.linkage_info {
119 invariant_violation!(
120 "Attempt to overwrite linkage info with context from {}. \
121 Existing linkage: {existing:#?}",
122 context.id(),
123 )
124 }
125
126 let linkage = LinkageInfo::from(context);
127 let storage_id = context.id();
128 let runtime_id = linkage.runtime_id;
129 self.linkage_info = Some(linkage);
130
131 if !self.past_contexts.borrow_mut().insert(storage_id) {
132 return Ok(runtime_id);
133 }
134
135 for TypeOrigin {
139 module_name,
140 datatype_name: struct_name,
141 package: defining_id,
142 } in context.type_origin_table()
143 {
144 let Ok(module_name) = Identifier::from_str(module_name) else {
145 invariant_violation!("Module name isn't an identifier: {module_name}");
146 };
147
148 let Ok(struct_name) = Identifier::from_str(struct_name) else {
149 invariant_violation!("Struct name isn't an identifier: {struct_name}");
150 };
151
152 let runtime_id = ModuleId::new(runtime_id, module_name);
153 self.add_type_origin(runtime_id, struct_name, *defining_id)?;
154 }
155
156 Ok(runtime_id)
157 }
158
159 pub fn original_package_id(&self) -> Option<AccountAddress> {
162 Some(self.linkage_info.as_ref()?.runtime_id)
163 }
164
165 fn get_cached_type_origin(
170 &self,
171 runtime_id: &ModuleId,
172 struct_: &IdentStr,
173 ) -> Option<AccountAddress> {
174 self.type_origin_cache
175 .borrow()
176 .get(runtime_id)?
177 .get(struct_)
178 .cloned()
179 }
180
181 fn add_type_origin(
184 &self,
185 runtime_id: ModuleId,
186 struct_: Identifier,
187 defining_id: ObjectID,
188 ) -> Result<(), ExecutionError> {
189 let mut cache = self.type_origin_cache.borrow_mut();
190 let module_cache = cache.entry(runtime_id.clone()).or_default();
191
192 match module_cache.entry(struct_) {
193 Entry::Vacant(entry) => {
194 entry.insert(AccountAddress::new(defining_id.into_bytes()));
195 }
196
197 Entry::Occupied(entry) => {
198 if entry.get().as_ref() != defining_id.as_bytes() {
199 invariant_violation!(
200 "Conflicting defining ID for {}::{}: {} and {}",
201 runtime_id,
202 entry.key(),
203 defining_id,
204 entry.get(),
205 );
206 }
207 }
208 }
209
210 Ok(())
211 }
212
213 pub(crate) fn link_context(&self) -> AccountAddress {
215 self.linkage_info
216 .as_ref()
217 .map_or(AccountAddress::ZERO, |l| l.storage_id)
218 }
219
220 pub(crate) fn relocate(&self, module_id: &ModuleId) -> Result<ModuleId, IotaError> {
222 let Some(linkage) = &self.linkage_info else {
223 invariant_violation!("No linkage context set while relocating {module_id}.")
224 };
225
226 if module_id.address() == &linkage.runtime_id {
230 return Ok(ModuleId::new(
231 linkage.storage_id,
232 module_id.name().to_owned(),
233 ));
234 }
235
236 let runtime_id = ObjectID::new(module_id.address().into_bytes());
237 let Some(upgrade) = linkage.link_table.get(&runtime_id) else {
238 invariant_violation!(
239 "Missing linkage for {runtime_id} in context {}, runtime_id is {}",
240 linkage.storage_id,
241 linkage.runtime_id
242 );
243 };
244
245 Ok(ModuleId::new(
246 AccountAddress::new(upgrade.upgraded_id.into_bytes()),
247 module_id.name().to_owned(),
248 ))
249 }
250
251 pub(crate) fn defining_module(
258 &self,
259 runtime_id: &ModuleId,
260 struct_: &IdentStr,
261 ) -> Result<ModuleId, IotaError> {
262 if self.linkage_info.is_none() {
263 invariant_violation!(
264 "No linkage context set for defining module query on {runtime_id}::{struct_}."
265 )
266 }
267
268 if let Some(cached) = self.get_cached_type_origin(runtime_id, struct_) {
269 return Ok(ModuleId::new(cached, runtime_id.name().to_owned()));
270 }
271
272 let storage_id = ObjectID::new(self.relocate(runtime_id)?.address().into_bytes());
273 let Some(package) = self.resolver.get_package_object(&storage_id)? else {
274 invariant_violation!("Missing dependent package in store: {storage_id}",)
275 };
276
277 for TypeOrigin {
278 module_name,
279 datatype_name: struct_name,
280 package,
281 } in package.move_package().type_origin_table()
282 {
283 if module_name == runtime_id.name().as_str() && struct_name == struct_.as_str() {
284 self.add_type_origin(runtime_id.clone(), struct_.to_owned(), *package)?;
285 return Ok(ModuleId::new(
286 AccountAddress::new(package.into_bytes()),
287 runtime_id.name().to_owned(),
288 ));
289 }
290 }
291
292 invariant_violation!(
293 "{runtime_id}::{struct_} not found in type origin table in {storage_id} (v{})",
294 package.move_package().version(),
295 )
296 }
297}
298
299impl From<&MovePackage> for LinkageInfo {
300 fn from(package: &MovePackage) -> Self {
301 Self {
302 storage_id: AccountAddress::new(package.id().into_bytes()),
303 runtime_id: AccountAddress::new(package.original_package_id().into_bytes()),
304 link_table: package.linkage_table().clone(),
305 }
306 }
307}
308
309impl LinkageResolver for LinkageView<'_> {
310 type Error = IotaError;
311
312 fn link_context(&self) -> AccountAddress {
313 LinkageView::link_context(self)
314 }
315
316 fn relocate(&self, module_id: &ModuleId) -> Result<ModuleId, Self::Error> {
317 LinkageView::relocate(self, module_id)
318 }
319
320 fn defining_module(
321 &self,
322 runtime_id: &ModuleId,
323 struct_: &IdentStr,
324 ) -> Result<ModuleId, Self::Error> {
325 LinkageView::defining_module(self, runtime_id, struct_)
326 }
327}
328
329impl ResourceResolver for LinkageView<'_> {
332 type Error = IotaError;
333
334 fn get_resource(
335 &self,
336 address: &AccountAddress,
337 type_: &StructTag,
338 ) -> Result<Option<Vec<u8>>, Self::Error> {
339 self.resolver.get_resource(address, type_)
340 }
341}
342
343impl ModuleResolver for LinkageView<'_> {
344 type Error = IotaError;
345
346 fn get_module(&self, id: &ModuleId) -> Result<Option<Vec<u8>>, Self::Error> {
347 get_module(self, id)
348 }
349}
350
351impl BackingPackageStore for LinkageView<'_> {
352 fn get_package_object(&self, package_id: &ObjectID) -> IotaResult<Option<PackageObject>> {
353 self.resolver.get_package_object(package_id)
354 }
355}