use crate::DCPError;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SignedToolDef {
pub tool_id: u32,
pub schema_hash: [u8; 32],
pub capabilities: u64,
pub signature: [u8; 64],
pub public_key: [u8; 32],
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SignedInvocation {
pub tool_id: u32,
pub nonce: u64,
pub timestamp: u64,
pub args_hash: [u8; 32],
pub signature: [u8; 64],
}
impl SignedToolDef {
pub const SIZE: usize = 144;
pub const SIGNED_BYTES_SIZE: usize = 44;
#[inline(always)]
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, DCPError> {
let bytes = bytes.as_ref();
if bytes.len() < Self::SIZE {
return Err(DCPError::InsufficientData);
}
if bytes.len() != Self::SIZE {
return Err(DCPError::ValidationFailed);
}
if bytes[36..40].iter().any(|&byte| byte != 0) {
return Err(DCPError::ValidationFailed);
}
let mut schema_hash = [0u8; 32];
schema_hash.copy_from_slice(&bytes[4..36]);
let mut signature = [0u8; 64];
signature.copy_from_slice(&bytes[48..112]);
let mut public_key = [0u8; 32];
public_key.copy_from_slice(&bytes[112..144]);
Ok(Self {
tool_id: u32::from_le_bytes(bytes[0..4].try_into().unwrap()),
schema_hash,
capabilities: u64::from_le_bytes(bytes[40..48].try_into().unwrap()),
signature,
public_key,
})
}
#[inline(always)]
pub fn as_bytes(&self) -> [u8; Self::SIZE] {
let mut bytes = [0u8; Self::SIZE];
bytes[0..4].copy_from_slice(&self.tool_id.to_le_bytes());
bytes[4..36].copy_from_slice(&self.schema_hash);
bytes[40..48].copy_from_slice(&self.capabilities.to_le_bytes());
bytes[48..112].copy_from_slice(&self.signature);
bytes[112..144].copy_from_slice(&self.public_key);
bytes
}
pub fn signed_bytes(&self) -> [u8; Self::SIGNED_BYTES_SIZE] {
let mut bytes = [0u8; Self::SIGNED_BYTES_SIZE];
bytes[0..4].copy_from_slice(&self.tool_id.to_le_bytes());
bytes[4..36].copy_from_slice(&self.schema_hash);
bytes[36..44].copy_from_slice(&self.capabilities.to_le_bytes());
bytes
}
}
impl SignedInvocation {
pub const SIZE: usize = 120;
pub const SIGNED_BYTES_SIZE: usize = 52;
#[inline(always)]
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, DCPError> {
let bytes = bytes.as_ref();
if bytes.len() < Self::SIZE {
return Err(DCPError::InsufficientData);
}
if bytes.len() != Self::SIZE {
return Err(DCPError::ValidationFailed);
}
if bytes[4..8].iter().any(|&byte| byte != 0) {
return Err(DCPError::ValidationFailed);
}
let mut args_hash = [0u8; 32];
args_hash.copy_from_slice(&bytes[24..56]);
let mut signature = [0u8; 64];
signature.copy_from_slice(&bytes[56..120]);
Ok(Self {
tool_id: u32::from_le_bytes(bytes[0..4].try_into().unwrap()),
nonce: u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
timestamp: u64::from_le_bytes(bytes[16..24].try_into().unwrap()),
args_hash,
signature,
})
}
#[inline(always)]
pub fn as_bytes(&self) -> [u8; Self::SIZE] {
let mut bytes = [0u8; Self::SIZE];
bytes[0..4].copy_from_slice(&self.tool_id.to_le_bytes());
bytes[8..16].copy_from_slice(&self.nonce.to_le_bytes());
bytes[16..24].copy_from_slice(&self.timestamp.to_le_bytes());
bytes[24..56].copy_from_slice(&self.args_hash);
bytes[56..120].copy_from_slice(&self.signature);
bytes
}
pub fn signed_bytes(&self) -> [u8; Self::SIGNED_BYTES_SIZE] {
let mut bytes = [0u8; Self::SIGNED_BYTES_SIZE];
bytes[0..4].copy_from_slice(&self.tool_id.to_le_bytes());
bytes[4..12].copy_from_slice(&self.nonce.to_le_bytes());
bytes[12..20].copy_from_slice(&self.timestamp.to_le_bytes());
bytes[20..52].copy_from_slice(&self.args_hash);
bytes
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_signed_tool_def_size() {
assert_eq!(std::mem::size_of::<SignedToolDef>(), SignedToolDef::SIZE);
}
#[test]
fn test_signed_invocation_size() {
assert_eq!(
std::mem::size_of::<SignedInvocation>(),
SignedInvocation::SIZE
);
}
#[test]
fn test_signed_tool_def_round_trip() {
let def = SignedToolDef {
tool_id: 42,
schema_hash: [0xAB; 32],
capabilities: 0x1234567890ABCDEF,
signature: [0xCD; 64],
public_key: [0xEF; 32],
};
let bytes = def.as_bytes();
let parsed = SignedToolDef::from_bytes(bytes).unwrap();
assert_eq!(parsed.tool_id, 42);
assert_eq!(parsed.schema_hash, [0xAB; 32]);
assert_eq!(parsed.capabilities, 0x1234567890ABCDEF);
assert_eq!(parsed.signature, [0xCD; 64]);
assert_eq!(parsed.public_key, [0xEF; 32]);
}
#[test]
fn test_signed_invocation_round_trip() {
let inv = SignedInvocation {
tool_id: 123,
nonce: 0xDEADBEEF,
timestamp: 1234567890,
args_hash: [0x11; 32],
signature: [0x22; 64],
};
let bytes = inv.as_bytes();
let parsed = SignedInvocation::from_bytes(bytes).unwrap();
assert_eq!(parsed.tool_id, 123);
assert_eq!(parsed.nonce, 0xDEADBEEF);
assert_eq!(parsed.timestamp, 1234567890);
assert_eq!(parsed.args_hash, [0x11; 32]);
assert_eq!(parsed.signature, [0x22; 64]);
}
#[test]
fn test_signed_bytes_length() {
let def = SignedToolDef {
tool_id: 0,
schema_hash: [0; 32],
capabilities: 0,
signature: [0; 64],
public_key: [0; 32],
};
assert_eq!(def.signed_bytes().len(), 44);
let inv = SignedInvocation {
tool_id: 0,
nonce: 0,
timestamp: 0,
args_hash: [0; 32],
signature: [0; 64],
};
assert_eq!(inv.signed_bytes().len(), 52);
}
}