31
31
tempfile:: { tempdir, TempDir } ,
32
32
} ;
33
33
34
+ const APPEND_CRATE_TO_ELF : bool = true ;
35
+
34
36
pub ( crate ) type Error = Box < dyn std:: error:: Error + Send + Sync + ' static > ;
35
37
36
38
#[ derive( Clone , Debug , Deserialize , Serialize ) ]
@@ -99,6 +101,8 @@ pub(crate) struct Program {
99
101
path : String ,
100
102
id : Pubkey ,
101
103
_tempdir : Arc < TempDir > ,
104
+ meta : PackageMetaData ,
105
+ crate_bytes : CrateTarGz ,
102
106
}
103
107
104
108
impl Program {
@@ -107,9 +111,17 @@ impl Program {
107
111
return Err ( "Signer doesn't match program ID" . into ( ) ) ;
108
112
}
109
113
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 ( ) )
111
115
. map_err ( |e| format ! ( "failed to read the program: {}" , e) ) ?;
112
116
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
+ }
113
125
let command_config = RPCCommandConfig :: new ( client. as_ref ( ) ) ;
114
126
115
127
process_deploy_program (
@@ -128,7 +140,7 @@ impl Program {
128
140
Ok ( ( ) )
129
141
}
130
142
131
- fn dump ( & self , client : Arc < Client > ) -> Result < ( ) , Error > {
143
+ fn dump ( & mut self , client : Arc < Client > ) -> Result < ( ) , Error > {
132
144
info ! ( "Fetching program {:?}" , self . id) ;
133
145
let command_config = RPCCommandConfig :: new ( client. as_ref ( ) ) ;
134
146
@@ -143,14 +155,42 @@ impl Program {
143
155
format ! ( "Failed to fetch the program: {}" , e)
144
156
} ) ?;
145
157
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
+ }
146
181
Ok ( ( ) )
147
182
}
148
183
149
184
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)
151
187
. ok ( )
152
188
. and_then ( |bytes| Pubkey :: try_from ( bytes) . ok ( ) )
153
189
}
190
+
191
+ fn program_id_to_crate_name ( id : Pubkey ) -> String {
192
+ format ! ( "sol-{}" , hex:: encode( id. to_bytes( ) ) )
193
+ }
154
194
}
155
195
156
196
impl From < & UnpackedCrate > for Program {
@@ -159,20 +199,23 @@ impl From<&UnpackedCrate> for Program {
159
199
path : value. program_path . clone ( ) ,
160
200
id : value. program_id ,
161
201
_tempdir : value. tempdir . clone ( ) ,
202
+ meta : value. meta . clone ( ) ,
203
+ crate_bytes : value. crate_bytes . clone ( ) ,
162
204
}
163
205
}
164
206
}
165
207
166
- pub ( crate ) struct CratePackage ( pub ( crate ) Bytes ) ;
208
+ #[ derive( Clone , Default ) ]
209
+ pub ( crate ) struct CrateTarGz ( pub ( crate ) Bytes ) ;
167
210
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 > {
170
213
let mut archive = Builder :: new ( Vec :: new ( ) ) ;
171
214
archive. mode ( HeaderMode :: Deterministic ) ;
172
215
173
- let base_path = UnpackedCrate :: make_path ( & value. tempdir , & value. meta , "out " ) ;
216
+ let base_path = UnpackedCrate :: make_path ( & value. tempdir , & value. meta , "" ) ;
174
217
archive. append_dir_all (
175
- format ! ( "{}-{}/out " , value. meta. name, value. meta. vers) ,
218
+ format ! ( "{}-{}/" , value. meta. name, value. meta. vers) ,
176
219
base_path,
177
220
) ?;
178
221
let data = archive. into_inner ( ) ?;
@@ -182,41 +225,50 @@ impl From<UnpackedCrate> for Result<CratePackage, Error> {
182
225
let mut zipped_data = Vec :: new ( ) ;
183
226
encoder. read_to_end ( & mut zipped_data) ?;
184
227
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)
186
245
}
187
246
}
188
247
248
+ pub ( crate ) struct CratePackage ( pub ( crate ) Bytes ) ;
249
+
189
250
pub ( crate ) struct UnpackedCrate {
190
251
meta : PackageMetaData ,
191
252
cksum : String ,
192
253
tempdir : Arc < TempDir > ,
193
254
program_path : String ,
194
255
program_id : Pubkey ,
195
256
keypair : Option < Keypair > ,
257
+ crate_bytes : CrateTarGz ,
196
258
}
197
259
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 ) ) ;
207
263
208
- let decoder = GzDecoder :: new ( crate_bytes. as_ref ( ) ) ;
264
+ let decoder = GzDecoder :: new ( crate_bytes. 0 . as_ref ( ) ) ;
209
265
let mut archive = Archive :: new ( decoder) ;
210
266
211
267
let tempdir = tempdir ( ) ?;
212
268
archive. unpack ( tempdir. path ( ) ) ?;
213
269
214
270
let lib_name = UnpackedCrate :: program_library_name ( & tempdir, & meta) ?;
215
271
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
-
220
272
let program_path =
221
273
UnpackedCrate :: make_path ( & tempdir, & meta, format ! ( "out/{}.so" , lib_name) )
222
274
. into_os_string ( )
@@ -237,8 +289,19 @@ impl From<CratePackage> for Result<UnpackedCrate, Error> {
237
289
program_path,
238
290
program_id : keypair. pubkey ( ) ,
239
291
keypair : Some ( keypair) ,
292
+ crate_bytes,
240
293
} )
241
294
}
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
+ }
242
305
}
243
306
244
307
impl UnpackedCrate {
@@ -262,36 +325,37 @@ impl UnpackedCrate {
262
325
}
263
326
264
327
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 ( ) ;
271
330
entry. cksum = format ! ( "{:x}" , Sha256 :: digest( & packed_crate. 0 ) ) ;
272
331
Ok ( entry)
273
332
}
274
333
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) ;
283
341
program. dump ( client) ?;
284
342
285
343
// Decompile the program
286
344
// Generate a Cargo.toml
287
345
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
+ }
289
353
}
290
354
291
- fn new_empty ( id : Pubkey ) -> Result < Self , Error > {
355
+ fn new_empty ( id : Pubkey , vers : & str ) -> Result < Self , Error > {
292
356
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 ( ) ,
295
359
deps : vec ! [ ] ,
296
360
features : BTreeMap :: new ( ) ,
297
361
authors : vec ! [ ] ,
@@ -328,6 +392,7 @@ impl UnpackedCrate {
328
392
program_path,
329
393
program_id : id,
330
394
keypair : None ,
395
+ crate_bytes : CrateTarGz :: default ( ) ,
331
396
} )
332
397
}
333
398
@@ -348,4 +413,22 @@ impl UnpackedCrate {
348
413
. ok_or ( "Failed to get module name" ) ?;
349
414
Ok ( library_name. to_string ( ) )
350
415
}
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
+ }
351
434
}
0 commit comments