-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add Codama IDL type * Refactor
- Loading branch information
Showing
4 changed files
with
92 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,75 +1,110 @@ | ||
#[cfg(feature = "parse")] | ||
use { | ||
flate2::bufread::ZlibDecoder, goblin::elf::Elf, serde_json::Value, std::fmt, std::io::Read, | ||
std::str::FromStr, | ||
flate2::bufread::ZlibDecoder, | ||
goblin::elf::Elf, | ||
serde_json::Value, | ||
std::io::Read, | ||
std::str::{from_utf8, FromStr}, | ||
}; | ||
|
||
/// Name of the section containing the IDL type value. | ||
pub const IDL_TYPE_SECTION: &str = ".idl.type"; | ||
|
||
/// Name of the section containing the IDL data. | ||
pub const IDL_DATA_SECTION: &str = ".idl.data"; | ||
|
||
const ANCHOR_IDL_TYPE: &str = "anchor"; | ||
const CODAMA_IDL_TYPE: &str = "codama"; | ||
|
||
/// Defines the IDL type. | ||
#[derive(Clone, Debug)] | ||
pub enum IdlType { | ||
Anchor, | ||
Kinobi, | ||
Codama, | ||
} | ||
|
||
impl IdlType { | ||
pub const fn as_str(&self) -> &'static str { | ||
match self { | ||
IdlType::Anchor => ANCHOR_IDL_TYPE, | ||
IdlType::Codama => CODAMA_IDL_TYPE, | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Display for IdlType { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
impl std::fmt::Display for IdlType { | ||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
match self { | ||
IdlType::Anchor => write!(f, "Anchor"), | ||
IdlType::Kinobi => write!(f, "Kinobi"), | ||
IdlType::Anchor => write!(f, "{ANCHOR_IDL_TYPE}"), | ||
IdlType::Codama => write!(f, "{CODAMA_IDL_TYPE}"), | ||
} | ||
} | ||
} | ||
|
||
impl FromStr for IdlType { | ||
impl std::str::FromStr for IdlType { | ||
type Err = &'static str; | ||
|
||
fn from_str(s: &str) -> Result<Self, &'static str> { | ||
match s.to_lowercase().as_str() { | ||
"anchor" => Ok(IdlType::Anchor), | ||
"kinobi" => Ok(IdlType::Kinobi), | ||
ANCHOR_IDL_TYPE => Ok(IdlType::Anchor), | ||
CODAMA_IDL_TYPE => Ok(IdlType::Codama), | ||
_ => Err("Invalid IDL type"), | ||
} | ||
} | ||
} | ||
|
||
fn get_section_name(idl_type: IdlType) -> String { | ||
match idl_type { | ||
IdlType::Anchor => ".solana.idl".to_string(), | ||
IdlType::Kinobi => ".kinobi.idl".to_string(), | ||
} | ||
} | ||
|
||
pub fn parse_idl_from_program_binary( | ||
buffer: &[u8], | ||
idl_type: IdlType, | ||
) -> goblin::error::Result<Value> { | ||
#[cfg(feature = "parse")] | ||
pub fn parse_idl_from_program_binary(buffer: &[u8]) -> goblin::error::Result<(IdlType, Value)> { | ||
let elf = Elf::parse(buffer)?; | ||
|
||
let section_name = get_section_name(idl_type); | ||
let mut idl_type: Option<IdlType> = None; | ||
let mut idl_json: Option<Value> = None; | ||
|
||
// Iterate over section headers and print information | ||
// Iterate over section headers and retrieve the IDL data. | ||
for sh in &elf.section_headers { | ||
let name = elf.shdr_strtab.get_at(sh.sh_name).unwrap_or("<invalid>"); | ||
if name == section_name { | ||
// Get offset of .solana.idl section data | ||
let offset = sh.sh_offset as usize; | ||
|
||
// Get offset & size of the compressed IDL bytes | ||
let _data_loc = &buffer[offset + 4..offset + 8]; | ||
let data_loc = u32::from_le_bytes(_data_loc.try_into().unwrap()); | ||
let _data_size = &buffer[offset + 8..offset + 16]; | ||
let data_size = u64::from_le_bytes(_data_size.try_into().unwrap()); | ||
|
||
let compressed_data = | ||
&buffer[data_loc as usize..(data_loc as u64 + data_size) as usize]; | ||
let mut d = ZlibDecoder::new(compressed_data); | ||
let mut decompressed_data = Vec::new(); | ||
d.read_to_end(&mut decompressed_data).unwrap(); | ||
|
||
let json: Value = serde_json::from_slice(&decompressed_data).unwrap(); | ||
return Ok(json); | ||
|
||
match name { | ||
IDL_DATA_SECTION => { | ||
let (location, size) = get_section_data_offset(buffer, sh.sh_offset as usize); | ||
|
||
let slice = &buffer[location..location + size]; | ||
let mut compressed_data = ZlibDecoder::new(slice); | ||
let mut data = Vec::new(); | ||
compressed_data.read_to_end(&mut data).unwrap(); | ||
|
||
idl_json = Some(serde_json::from_slice(&data).unwrap()); | ||
} | ||
IDL_TYPE_SECTION => { | ||
let (location, size) = get_section_data_offset(buffer, sh.sh_offset as usize); | ||
let slice = &buffer[location..location + size]; | ||
|
||
idl_type = Some(IdlType::from_str(from_utf8(slice).unwrap()).unwrap()); | ||
} | ||
// Ignore other sections. | ||
_ => (), | ||
} | ||
} | ||
Err(goblin::error::Error::Malformed( | ||
"Could not find .solana.idl section".to_string(), | ||
)) | ||
|
||
if idl_type.is_some() && idl_json.is_some() { | ||
#[allow(clippy::unnecessary_unwrap)] | ||
Ok((idl_type.unwrap(), idl_json.unwrap())) | ||
} else { | ||
// Returns an error if we could not find the IDL information. | ||
Err(goblin::error::Error::Malformed( | ||
"Could not find .idl.* sections".to_string(), | ||
)) | ||
} | ||
} | ||
|
||
#[cfg(feature = "parse")] | ||
#[inline(always)] | ||
fn get_section_data_offset(buffer: &[u8], offset: usize) -> (usize, usize) { | ||
let slice = &buffer[offset + 4..offset + 8]; | ||
let location = u32::from_le_bytes(slice.try_into().unwrap()); | ||
|
||
let slice = &buffer[offset + 8..offset + 16]; | ||
let size = u64::from_le_bytes(slice.try_into().unwrap()); | ||
|
||
(location as usize, size as usize) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters