iota_package_management/
lib.rs1use std::{
6 collections::HashMap,
7 fs::File,
8 path::{Path, PathBuf},
9 str::FromStr,
10};
11
12use anyhow::bail;
13use iota_sdk_types::{Address, ObjectId};
14use move_package::{
15 lock_file::{self, LockFile, schema::ManagedPackage},
16 resolution::resolution_graph::Package,
17 source_package::layout::SourcePackageLayout,
18};
19use move_symbol_pool::Symbol;
20
21pub mod system_package_versions;
22
23const PUBLISHED_AT_MANIFEST_FIELD: &str = "published-at";
24
25pub enum LockCommand {
26 Publish,
27 Upgrade,
28}
29
30#[derive(thiserror::Error, Debug, Clone)]
31pub enum PublishedAtError {
32 #[error("The 'published-at' field in Move.toml or Move.lock is invalid: {0:?}")]
33 Invalid(String),
34
35 #[error("The 'published-at' field is not present in Move.toml or Move.lock")]
36 NotPresent,
37
38 #[error(
39 "Conflicting 'published-at' addresses between Move.toml -- {id_manifest} -- and \
40 Move.lock -- {id_lock}"
41 )]
42 Conflict {
43 id_lock: ObjectId,
44 id_manifest: ObjectId,
45 },
46}
47
48pub fn update_lock_file_with_package_id(
56 chain_identifier: String,
57 env_alias: &str,
58 command: LockCommand,
59 install_dir: Option<PathBuf>,
60 lock_file: Option<PathBuf>,
61 original_id: ObjectId,
62 version: u64,
63) -> Result<(), anyhow::Error> {
64 let Some(lock_file) = lock_file else {
65 bail!(
66 "Expected a `Move.lock` file to exist after publishing \
67 package, but none found. Consider running `iota move build` to \
68 generate the `Move.lock` file in the package directory."
69 )
70 };
71 let install_dir = install_dir.unwrap_or(PathBuf::from("."));
72
73 let mut lock = LockFile::from(install_dir, &lock_file)?;
74 match command {
75 LockCommand::Publish => lock_file::schema::update_managed_address(
76 &mut lock,
77 env_alias,
78 lock_file::schema::ManagedAddressUpdate::Published {
79 chain_id: chain_identifier,
80 original_id: original_id.to_string(),
81 },
82 ),
83 LockCommand::Upgrade => lock_file::schema::update_managed_address(
84 &mut lock,
85 env_alias,
86 lock_file::schema::ManagedAddressUpdate::Upgraded {
87 latest_id: original_id.to_string(),
88 version,
89 },
90 ),
91 }?;
92 lock.commit(lock_file)?;
93 Ok(())
94}
95
96pub fn set_package_id(
104 package_path: &Path,
105 install_dir: Option<PathBuf>,
106 chain_id: &String,
107 id: Address,
108) -> Result<Option<Address>, anyhow::Error> {
109 let lock_file_path = package_path.join(SourcePackageLayout::Lock.path());
110 let Ok(mut lock_file) = File::open(lock_file_path.clone()) else {
111 return Ok(None);
112 };
113 let managed_package = ManagedPackage::read(&mut lock_file)
114 .ok()
115 .and_then(|m| m.into_iter().find(|(_, v)| v.chain_id == *chain_id));
116 let Some((env, v)) = managed_package else {
117 return Ok(None);
118 };
119 let install_dir = install_dir.unwrap_or(PathBuf::from("."));
120 let lock_for_update = LockFile::from(install_dir, &lock_file_path);
121 let Ok(mut lock_for_update) = lock_for_update else {
122 return Ok(None);
123 };
124 lock_file::schema::set_original_id(&mut lock_for_update, &env, &id.to_canonical_string(true))?;
125 lock_for_update.commit(lock_file_path)?;
126 let id = Address::from_str(&v.original_published_id)?;
127 Ok(Some(id))
128}
129
130pub fn resolve_published_id(
137 package: &Package,
138 chain_id: Option<String>,
139) -> Result<ObjectId, PublishedAtError> {
140 let published_id_in_manifest = manifest_published_at(package);
143
144 match published_id_in_manifest {
145 Ok(_) | Err(PublishedAtError::NotPresent) => { }
146 Err(e) => {
147 return Err(e);
148 }
149 }
150
151 let lock = package.package_path.join(SourcePackageLayout::Lock.path());
152 let Ok(mut lock_file) = File::open(lock) else {
153 return published_id_in_manifest;
154 };
155
156 let id_in_lock_for_chain_id =
158 lock_published_at(ManagedPackage::read(&mut lock_file).ok(), chain_id.as_ref());
159
160 match (id_in_lock_for_chain_id, published_id_in_manifest) {
161 (Ok(id_lock), Ok(id_manifest)) if id_lock != id_manifest => {
162 Err(PublishedAtError::Conflict {
163 id_lock,
164 id_manifest,
165 })
166 }
167
168 (Ok(id), _) | (_, Ok(id)) => Ok(id),
169
170 (from_lock, Err(_)) => from_lock,
174 }
175}
176
177fn manifest_published_at(package: &Package) -> Result<ObjectId, PublishedAtError> {
178 let Some(value) = package
179 .source_package
180 .package
181 .custom_properties
182 .get(&Symbol::from(PUBLISHED_AT_MANIFEST_FIELD))
183 else {
184 return Err(PublishedAtError::NotPresent);
185 };
186
187 let id =
188 ObjectId::from_str(value.as_str()).map_err(|_| PublishedAtError::Invalid(value.clone()))?;
189
190 if id == ObjectId::ZERO {
191 Err(PublishedAtError::NotPresent)
192 } else {
193 Ok(id)
194 }
195}
196
197fn lock_published_at(
198 lock: Option<HashMap<String, ManagedPackage>>,
199 chain_id: Option<&String>,
200) -> Result<ObjectId, PublishedAtError> {
201 let (Some(lock), Some(chain_id)) = (lock, chain_id) else {
202 return Err(PublishedAtError::NotPresent);
203 };
204
205 let managed_package = lock
206 .into_values()
207 .find(|v| v.chain_id == *chain_id)
208 .ok_or(PublishedAtError::NotPresent)?;
209
210 let id = ObjectId::from_str(managed_package.latest_published_id.as_str())
211 .map_err(|_| PublishedAtError::Invalid(managed_package.latest_published_id.clone()))?;
212
213 if id == ObjectId::ZERO {
214 Err(PublishedAtError::NotPresent)
215 } else {
216 Ok(id)
217 }
218}