use super::*;
impl<N: Network> FromBytes for Program<N> {
fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
let version = u16::read_le(&mut reader)?;
if version != 0 {
return Err(error("Invalid program version"));
}
let id = ProgramID::read_le(&mut reader)?;
let mut program = Program::new(id).map_err(|e| error(e.to_string()))?;
let imports_len = u8::read_le(&mut reader)?;
for _ in 0..imports_len {
program.add_import(Import::read_le(&mut reader)?).map_err(|e| error(e.to_string()))?;
}
let components_len = u16::read_le(&mut reader)?;
for _ in 0..components_len {
let variant = u8::read_le(&mut reader)?;
match variant {
0 => program.add_mapping(Mapping::read_le(&mut reader)?).map_err(|e| error(e.to_string()))?,
1 => program.add_interface(Interface::read_le(&mut reader)?).map_err(|e| error(e.to_string()))?,
2 => program.add_record(RecordType::read_le(&mut reader)?).map_err(|e| error(e.to_string()))?,
3 => program.add_closure(Closure::read_le(&mut reader)?).map_err(|e| error(e.to_string()))?,
4 => program.add_function(Function::read_le(&mut reader)?).map_err(|e| error(e.to_string()))?,
_ => return Err(error(format!("Failed to parse program. Invalid component variant '{variant}'"))),
}
}
Ok(program)
}
}
impl<N: Network> ToBytes for Program<N> {
fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
0u16.write_le(&mut writer)?;
self.id.write_le(&mut writer)?;
(self.imports.len() as u8).write_le(&mut writer)?;
for import in self.imports.values() {
import.write_le(&mut writer)?;
}
(self.identifiers.len() as u16).write_le(&mut writer)?;
for (identifier, definition) in self.identifiers.iter() {
match definition {
ProgramDefinition::Mapping => match self.mappings.get(identifier) {
Some(mapping) => {
0u8.write_le(&mut writer)?;
mapping.write_le(&mut writer)?;
}
None => return Err(error(format!("Mapping '{identifier}' is not defined"))),
},
ProgramDefinition::Interface => match self.interfaces.get(identifier) {
Some(interface) => {
1u8.write_le(&mut writer)?;
interface.write_le(&mut writer)?;
}
None => return Err(error(format!("Interface '{identifier}' is not defined."))),
},
ProgramDefinition::Record => match self.records.get(identifier) {
Some(record) => {
2u8.write_le(&mut writer)?;
record.write_le(&mut writer)?;
}
None => return Err(error(format!("Record '{identifier}' is not defined."))),
},
ProgramDefinition::Closure => match self.closures.get(identifier) {
Some(closure) => {
3u8.write_le(&mut writer)?;
closure.write_le(&mut writer)?;
}
None => return Err(error(format!("Closure '{identifier}' is not defined."))),
},
ProgramDefinition::Function => match self.functions.get(identifier) {
Some(function) => {
4u8.write_le(&mut writer)?;
function.write_le(&mut writer)?;
}
None => return Err(error(format!("Function '{identifier}' is not defined."))),
},
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use console::network::Testnet3;
type CurrentNetwork = Testnet3;
#[test]
fn test_bytes() -> Result<()> {
let program = r"
program token.aleo;
record token:
owner as address.private;
gates as u64.private;
token_amount as u64.private;
function compute:
input r0 as token.record;
add r0.token_amount r0.token_amount into r1;
output r1 as u64.private;";
let (string, expected) = Program::<CurrentNetwork>::parse(program).unwrap();
assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
let expected_bytes = expected.to_bytes_le()?;
let candidate = Program::<CurrentNetwork>::from_bytes_le(&expected_bytes)?;
assert_eq!(expected, candidate);
assert_eq!(expected_bytes, candidate.to_bytes_le()?);
Ok(())
}
}