use core::fmt;
pub const METHOD: &str = "raw_rand";
pub const MAX_REPLY_BYTES: usize = 74;
pub const fn encode() -> &'static [u8; 6] {
crate::candid_args::empty()
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DecodeError {
TooLarge,
Truncated,
InvalidMagic,
TypeTable,
ResultArity,
ResultType,
BlobLength,
TrailingBytes,
}
impl fmt::Display for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::TooLarge => "raw_rand reply exceeds 74 bytes",
Self::Truncated => "truncated raw_rand reply",
Self::InvalidMagic => "invalid raw_rand Candid magic",
Self::TypeTable => "raw_rand requires one vec nat8 type-table entry",
Self::ResultArity => "raw_rand requires exactly one result",
Self::ResultType => "raw_rand result must reference type zero",
Self::BlobLength => "raw_rand requires a 32-byte blob",
Self::TrailingBytes => "trailing bytes after raw_rand result",
})
}
}
impl core::error::Error for DecodeError {}
pub fn decode(bytes: &[u8]) -> Result<[u8; 32], DecodeError> {
if bytes.len() > MAX_REPLY_BYTES {
return Err(DecodeError::TooLarge);
}
let (magic, mut rest) = bytes.split_at_checked(4).ok_or(DecodeError::Truncated)?;
if magic != b"DIDL" {
return Err(DecodeError::InvalidMagic);
}
expect_leb(&mut rest, 1, DecodeError::TypeTable)?;
let (opcodes, after_types) = rest.split_at_checked(2).ok_or(DecodeError::Truncated)?;
if opcodes != b"\x6d\x7b" {
return Err(DecodeError::TypeTable);
}
rest = after_types;
expect_leb(&mut rest, 1, DecodeError::ResultArity)?;
expect_leb(&mut rest, 0, DecodeError::ResultType)?;
expect_leb(&mut rest, 32, DecodeError::BlobLength)?;
let (random, trailing) = rest.split_at_checked(32).ok_or(DecodeError::Truncated)?;
if !trailing.is_empty() {
return Err(DecodeError::TrailingBytes);
}
random.try_into().map_err(|_| DecodeError::Truncated)
}
fn expect_leb(bytes: &mut &[u8], mut expected: u8, error: DecodeError) -> Result<(), DecodeError> {
for _ in 0..9 {
let (&byte, rest) = bytes.split_first().ok_or(DecodeError::Truncated)?;
*bytes = rest;
if byte & 0x7f != expected {
return Err(error);
}
expected >>= 7;
if byte & 0x80 == 0 {
return Ok(());
}
}
Err(error)
}
#[cfg(test)]
mod tests;