Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit bdfb644

Browse files
authored
Append crate to ELF file while deploying program (#33849)
* Append crate to ELF file while deploying program * review feedback
1 parent bd1080b commit bdfb644

File tree

2 files changed

+128
-45
lines changed

2 files changed

+128
-45
lines changed

cargo-registry/src/crate_handler.rs

Lines changed: 124 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ use {
3131
tempfile::{tempdir, TempDir},
3232
};
3333

34+
const APPEND_CRATE_TO_ELF: bool = true;
35+
3436
pub(crate) type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
3537

3638
#[derive(Clone, Debug, Deserialize, Serialize)]
@@ -99,6 +101,8 @@ pub(crate) struct Program {
99101
path: String,
100102
id: Pubkey,
101103
_tempdir: Arc<TempDir>,
104+
meta: PackageMetaData,
105+
crate_bytes: CrateTarGz,
102106
}
103107

104108
impl Program {
@@ -107,9 +111,17 @@ impl Program {
107111
return Err("Signer doesn't match program ID".into());
108112
}
109113

110-
let program_data = read_and_verify_elf(self.path.as_ref())
114+
let mut program_data = read_and_verify_elf(self.path.as_ref())
111115
.map_err(|e| format!("failed to read the program: {}", e))?;
112116

117+
if APPEND_CRATE_TO_ELF {
118+
let program_id_str = Program::program_id_to_crate_name(self.id);
119+
let crate_tar_gz =
120+
CrateTarGz::new_rebased(&self.crate_bytes, &self.meta, &program_id_str)?;
121+
let crate_len = u32::to_le_bytes(crate_tar_gz.0.len() as u32);
122+
program_data.extend_from_slice(&crate_tar_gz.0);
123+
program_data.extend_from_slice(&crate_len);
124+
}
113125
let command_config = RPCCommandConfig::new(client.as_ref());
114126

115127
process_deploy_program(
@@ -128,7 +140,7 @@ impl Program {
128140
Ok(())
129141
}
130142

131-
fn dump(&self, client: Arc<Client>) -> Result<(), Error> {
143+
fn dump(&mut self, client: Arc<Client>) -> Result<(), Error> {
132144
info!("Fetching program {:?}", self.id);
133145
let command_config = RPCCommandConfig::new(client.as_ref());
134146

@@ -143,14 +155,42 @@ impl Program {
143155
format!("Failed to fetch the program: {}", e)
144156
})?;
145157

158+
if APPEND_CRATE_TO_ELF {
159+
let Ok(buffer) = fs::read(&self.path) else {
160+
return Err("Failed to read the program file".into());
161+
};
162+
163+
let data = Bytes::from(buffer);
164+
165+
let data_len = data.len();
166+
let sizeof_length = size_of::<u32>();
167+
168+
// The crate length is at the tail of the data buffer, as 4 LE bytes.
169+
let length_le = data.slice(data_len.saturating_sub(sizeof_length)..data_len);
170+
let length =
171+
u32::from_le_bytes(length_le.deref().try_into().expect("Failed to read length"));
172+
173+
let crate_start = data_len
174+
.saturating_sub(sizeof_length)
175+
.saturating_sub(length as usize);
176+
let crate_end = data_len.saturating_sub(sizeof_length);
177+
178+
let crate_bytes = CrateTarGz(Bytes::copy_from_slice(&data[crate_start..crate_end]));
179+
self.crate_bytes = crate_bytes;
180+
}
146181
Ok(())
147182
}
148183

149184
pub(crate) fn crate_name_to_program_id(crate_name: &str) -> Option<Pubkey> {
150-
hex::decode(crate_name)
185+
let (_, id_str) = crate_name.split_once('-')?;
186+
hex::decode(id_str)
151187
.ok()
152188
.and_then(|bytes| Pubkey::try_from(bytes).ok())
153189
}
190+
191+
fn program_id_to_crate_name(id: Pubkey) -> String {
192+
format!("sol-{}", hex::encode(id.to_bytes()))
193+
}
154194
}
155195

156196
impl From<&UnpackedCrate> for Program {
@@ -159,20 +199,23 @@ impl From<&UnpackedCrate> for Program {
159199
path: value.program_path.clone(),
160200
id: value.program_id,
161201
_tempdir: value.tempdir.clone(),
202+
meta: value.meta.clone(),
203+
crate_bytes: value.crate_bytes.clone(),
162204
}
163205
}
164206
}
165207

166-
pub(crate) struct CratePackage(pub(crate) Bytes);
208+
#[derive(Clone, Default)]
209+
pub(crate) struct CrateTarGz(pub(crate) Bytes);
167210

168-
impl From<UnpackedCrate> for Result<CratePackage, Error> {
169-
fn from(value: UnpackedCrate) -> Self {
211+
impl CrateTarGz {
212+
fn new(value: UnpackedCrate) -> Result<Self, Error> {
170213
let mut archive = Builder::new(Vec::new());
171214
archive.mode(HeaderMode::Deterministic);
172215

173-
let base_path = UnpackedCrate::make_path(&value.tempdir, &value.meta, "out");
216+
let base_path = UnpackedCrate::make_path(&value.tempdir, &value.meta, "");
174217
archive.append_dir_all(
175-
format!("{}-{}/out", value.meta.name, value.meta.vers),
218+
format!("{}-{}/", value.meta.name, value.meta.vers),
176219
base_path,
177220
)?;
178221
let data = archive.into_inner()?;
@@ -182,41 +225,50 @@ impl From<UnpackedCrate> for Result<CratePackage, Error> {
182225
let mut zipped_data = Vec::new();
183226
encoder.read_to_end(&mut zipped_data)?;
184227

185-
Ok(CratePackage(Bytes::from(zipped_data)))
228+
Ok(CrateTarGz(Bytes::from(zipped_data)))
229+
}
230+
231+
fn new_rebased(&self, meta: &PackageMetaData, target_base: &str) -> Result<Self, Error> {
232+
let mut unpacked = UnpackedCrate::decompress(self.clone(), meta.clone())?;
233+
234+
let name = Program::program_id_to_crate_name(unpacked.program_id);
235+
UnpackedCrate::fixup_toml(&unpacked.tempdir, "Cargo.toml.orig", &unpacked.meta, &name)?;
236+
UnpackedCrate::fixup_toml(&unpacked.tempdir, "Cargo.toml", &unpacked.meta, &name)?;
237+
238+
let source_path = UnpackedCrate::make_path(&unpacked.tempdir, &unpacked.meta, "");
239+
unpacked.meta.name = target_base.to_string();
240+
let target_path = UnpackedCrate::make_path(&unpacked.tempdir, &unpacked.meta, "");
241+
fs::rename(source_path, target_path.clone())
242+
.map_err(|_| "Failed to rename the crate folder")?;
243+
244+
Self::new(unpacked)
186245
}
187246
}
188247

248+
pub(crate) struct CratePackage(pub(crate) Bytes);
249+
189250
pub(crate) struct UnpackedCrate {
190251
meta: PackageMetaData,
191252
cksum: String,
192253
tempdir: Arc<TempDir>,
193254
program_path: String,
194255
program_id: Pubkey,
195256
keypair: Option<Keypair>,
257+
crate_bytes: CrateTarGz,
196258
}
197259

198-
impl From<CratePackage> for Result<UnpackedCrate, Error> {
199-
fn from(value: CratePackage) -> Self {
200-
let bytes = value.0;
201-
let (meta, offset) = PackageMetaData::new(&bytes)?;
202-
203-
let (_crate_file_length, length_size) =
204-
PackageMetaData::read_u32_length(&bytes.slice(offset..))?;
205-
let crate_bytes = bytes.slice(offset.saturating_add(length_size)..);
206-
let cksum = format!("{:x}", Sha256::digest(&crate_bytes));
260+
impl UnpackedCrate {
261+
fn decompress(crate_bytes: CrateTarGz, meta: PackageMetaData) -> Result<Self, Error> {
262+
let cksum = format!("{:x}", Sha256::digest(&crate_bytes.0));
207263

208-
let decoder = GzDecoder::new(crate_bytes.as_ref());
264+
let decoder = GzDecoder::new(crate_bytes.0.as_ref());
209265
let mut archive = Archive::new(decoder);
210266

211267
let tempdir = tempdir()?;
212268
archive.unpack(tempdir.path())?;
213269

214270
let lib_name = UnpackedCrate::program_library_name(&tempdir, &meta)?;
215271

216-
let base_path = UnpackedCrate::make_path(&tempdir, &meta, "out");
217-
fs::create_dir_all(base_path)
218-
.map_err(|_| "Failed to create the base directory for output")?;
219-
220272
let program_path =
221273
UnpackedCrate::make_path(&tempdir, &meta, format!("out/{}.so", lib_name))
222274
.into_os_string()
@@ -237,8 +289,19 @@ impl From<CratePackage> for Result<UnpackedCrate, Error> {
237289
program_path,
238290
program_id: keypair.pubkey(),
239291
keypair: Some(keypair),
292+
crate_bytes,
240293
})
241294
}
295+
296+
pub(crate) fn unpack(value: CratePackage) -> Result<Self, Error> {
297+
let bytes = value.0;
298+
let (meta, offset) = PackageMetaData::new(&bytes)?;
299+
300+
let (_crate_file_length, length_size) =
301+
PackageMetaData::read_u32_length(&bytes.slice(offset..))?;
302+
let crate_bytes = CrateTarGz(bytes.slice(offset.saturating_add(length_size)..));
303+
UnpackedCrate::decompress(crate_bytes, meta)
304+
}
242305
}
243306

244307
impl UnpackedCrate {
@@ -262,36 +325,37 @@ impl UnpackedCrate {
262325
}
263326

264327
pub(crate) fn fetch_index(id: Pubkey, client: Arc<Client>) -> Result<IndexEntry, Error> {
265-
let (_program, unpacked_crate) = Self::fetch_program(id, client)?;
266-
let mut entry: IndexEntry = unpacked_crate.meta.clone().into();
267-
268-
let packed_crate: Result<CratePackage, Error> = UnpackedCrate::into(unpacked_crate);
269-
let packed_crate = packed_crate?;
270-
328+
let (packed_crate, meta) = Self::fetch(id, "0.1.0", client)?;
329+
let mut entry: IndexEntry = meta.into();
271330
entry.cksum = format!("{:x}", Sha256::digest(&packed_crate.0));
272331
Ok(entry)
273332
}
274333

275-
pub(crate) fn fetch(id: Pubkey, client: Arc<Client>) -> Result<CratePackage, Error> {
276-
let (_program, unpacked_crate) = Self::fetch_program(id, client)?;
277-
UnpackedCrate::into(unpacked_crate)
278-
}
279-
280-
fn fetch_program(id: Pubkey, client: Arc<Client>) -> Result<(Program, UnpackedCrate), Error> {
281-
let crate_obj = Self::new_empty(id)?;
282-
let program = Program::from(&crate_obj);
334+
pub(crate) fn fetch(
335+
id: Pubkey,
336+
vers: &str,
337+
client: Arc<Client>,
338+
) -> Result<(CrateTarGz, PackageMetaData), Error> {
339+
let crate_obj = Self::new_empty(id, vers)?;
340+
let mut program = Program::from(&crate_obj);
283341
program.dump(client)?;
284342

285343
// Decompile the program
286344
// Generate a Cargo.toml
287345

288-
Ok((program, crate_obj))
346+
let meta = crate_obj.meta.clone();
347+
348+
if APPEND_CRATE_TO_ELF {
349+
Ok((program.crate_bytes, meta))
350+
} else {
351+
CrateTarGz::new(crate_obj).map(|file| (file, meta))
352+
}
289353
}
290354

291-
fn new_empty(id: Pubkey) -> Result<Self, Error> {
355+
fn new_empty(id: Pubkey, vers: &str) -> Result<Self, Error> {
292356
let meta = PackageMetaData {
293-
name: hex::encode(id.to_bytes()),
294-
vers: "0.1.0".to_string(),
357+
name: Program::program_id_to_crate_name(id),
358+
vers: vers.to_string(),
295359
deps: vec![],
296360
features: BTreeMap::new(),
297361
authors: vec![],
@@ -328,6 +392,7 @@ impl UnpackedCrate {
328392
program_path,
329393
program_id: id,
330394
keypair: None,
395+
crate_bytes: CrateTarGz::default(),
331396
})
332397
}
333398

@@ -348,4 +413,22 @@ impl UnpackedCrate {
348413
.ok_or("Failed to get module name")?;
349414
Ok(library_name.to_string())
350415
}
416+
417+
fn fixup_toml(
418+
tempdir: &TempDir,
419+
cargo_toml_name: &str,
420+
meta: &PackageMetaData,
421+
name: &str,
422+
) -> Result<(), Error> {
423+
let toml_orig_path = Self::make_path(tempdir, meta, cargo_toml_name);
424+
let toml_content = fs::read_to_string(&toml_orig_path)?;
425+
let mut toml = toml_content.parse::<toml::Table>()?;
426+
toml.get_mut("package")
427+
.and_then(|v| v.get_mut("name"))
428+
.map(|v| *v = toml::Value::String(name.to_string()))
429+
.ok_or("Failed to set package name")?;
430+
431+
fs::write(toml_orig_path, toml.to_string())?;
432+
Ok(())
433+
}
351434
}

cargo-registry/src/main.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl CargoRegistryService {
3838

3939
match bytes {
4040
Ok(data) => {
41-
let Ok(crate_object) = CratePackage(data).into() else {
41+
let Ok(crate_object) = UnpackedCrate::unpack(CratePackage(data)) else {
4242
return response_builder::error_response(
4343
hyper::StatusCode::INTERNAL_SERVER_ERROR,
4444
"Failed to parse the crate information",
@@ -83,7 +83,7 @@ impl CargoRegistryService {
8383
_request: &hyper::Request<hyper::Body>,
8484
client: Arc<Client>,
8585
) -> hyper::Response<hyper::Body> {
86-
let Some((path, crate_name, _version)) = Self::get_crate_name_and_version(path) else {
86+
let Some((path, crate_name, version)) = Self::get_crate_name_and_version(path) else {
8787
return response_builder::error_in_parsing();
8888
};
8989

@@ -92,10 +92,10 @@ impl CargoRegistryService {
9292
}
9393

9494
let package = Program::crate_name_to_program_id(crate_name)
95-
.and_then(|id| UnpackedCrate::fetch(id, client).ok());
95+
.and_then(|id| UnpackedCrate::fetch(id, version, client).ok());
9696

9797
// Return the package to the caller in the response
98-
if let Some(package) = package {
98+
if let Some((package, _meta)) = package {
9999
response_builder::success_response_bytes(package.0)
100100
} else {
101101
response_builder::error_response(

0 commit comments

Comments
 (0)