use crate::{
builder::{BuilderError, InteropService},
codec::Decoder,
neo_crypto::utils::ToHexString,
Bytes, OpCode, OperandSize,
};
pub struct ScriptReader;
impl ScriptReader {
pub fn get_interop_service_code(_hash: String) -> Option<InteropService> {
InteropService::from_hash(_hash)
}
pub fn convert_to_op_code_string(script: &Bytes) -> String {
let mut reader = Decoder::new(script);
let mut result = String::new();
while reader.pointer().clone() < script.len() {
if let Ok(op_code) = OpCode::try_from(reader.read_u8()) {
result.push_str(&format!("{:?}", op_code).to_uppercase());
if let Some(size) = op_code.operand_size() {
if size.size().clone() > 0 {
match reader.read_bytes(size.size().clone() as usize) {
Ok(bytes) => {
result.push_str(&format!(" {}", bytes.to_hex_string()));
},
Err(_) => {
result.push_str(" <invalid operand>");
result.push('\n');
break;
},
}
} else if size.prefix_size().clone() > 0 {
let prefix_size = match Self::get_prefix_size(&mut reader, size) {
Ok(prefix_size) => prefix_size,
Err(_) => {
result.push_str(" <invalid operand prefix>");
result.push('\n');
break;
},
};
result.push_str(&format!(" {}", prefix_size));
match reader.read_bytes(prefix_size) {
Ok(bytes) => {
result.push_str(&format!(" {}", bytes.to_hex_string()));
},
Err(_) => {
result.push_str(" <invalid operand>");
result.push('\n');
break;
},
}
}
}
result.push('\n');
}
}
result
}
fn get_prefix_size(reader: &mut Decoder, size: OperandSize) -> Result<usize, BuilderError> {
match size.prefix_size() {
1 => Ok(reader.read_bytes(1)?[0] as usize),
2 => {
let value = reader.read_i16()?;
if value < 0 {
return Err(BuilderError::IllegalArgument(
"Operand prefix size cannot be negative".to_string(),
));
}
Ok(value as usize)
},
4 => {
let value = reader.read_i32()?;
if value < 0 {
return Err(BuilderError::IllegalArgument(
"Operand prefix size cannot be negative".to_string(),
));
}
Ok(value as usize)
},
_ => Err(BuilderError::UnsupportedOperation(
"Only operand prefix sizes 1, 2, and 4 are supported".to_string(),
)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_convert_to_op_code_string() {
let script =
hex::decode("0c0548656c6c6f0c05576f726c642150419bf667ce41e63f18841140").unwrap();
let expected_op_code_string = "PUSHDATA1 5 48656c6c6f\nPUSHDATA1 5 576f726c64\nNOP\nSWAP\nSYSCALL 9bf667ce\nSYSCALL e63f1884\nPUSH1\nRET\n";
let op_code_string = ScriptReader::convert_to_op_code_string(&script);
assert_eq!(op_code_string.as_str(), expected_op_code_string);
}
}