1use crate::DCPError;
4
5#[repr(C)]
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct SignedToolDef {
9 pub tool_id: u32,
11 pub schema_hash: [u8; 32],
13 pub capabilities: u64,
15 pub signature: [u8; 64],
17 pub public_key: [u8; 32],
19}
20
21#[repr(C)]
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct SignedInvocation {
25 pub tool_id: u32,
27 pub nonce: u64,
29 pub timestamp: u64,
31 pub args_hash: [u8; 32],
33 pub signature: [u8; 64],
35}
36
37impl SignedToolDef {
38 pub const SIZE: usize = 144; pub const SIGNED_BYTES_SIZE: usize = 44;
43
44 #[inline(always)]
46 pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, DCPError> {
47 let bytes = bytes.as_ref();
48 if bytes.len() < Self::SIZE {
49 return Err(DCPError::InsufficientData);
50 }
51 if bytes.len() != Self::SIZE {
52 return Err(DCPError::ValidationFailed);
53 }
54 if bytes[36..40].iter().any(|&byte| byte != 0) {
55 return Err(DCPError::ValidationFailed);
56 }
57
58 let mut schema_hash = [0u8; 32];
59 schema_hash.copy_from_slice(&bytes[4..36]);
60 let mut signature = [0u8; 64];
61 signature.copy_from_slice(&bytes[48..112]);
62 let mut public_key = [0u8; 32];
63 public_key.copy_from_slice(&bytes[112..144]);
64
65 Ok(Self {
66 tool_id: u32::from_le_bytes(bytes[0..4].try_into().unwrap()),
67 schema_hash,
68 capabilities: u64::from_le_bytes(bytes[40..48].try_into().unwrap()),
69 signature,
70 public_key,
71 })
72 }
73
74 #[inline(always)]
76 pub fn as_bytes(&self) -> [u8; Self::SIZE] {
77 let mut bytes = [0u8; Self::SIZE];
78 bytes[0..4].copy_from_slice(&self.tool_id.to_le_bytes());
79 bytes[4..36].copy_from_slice(&self.schema_hash);
80 bytes[40..48].copy_from_slice(&self.capabilities.to_le_bytes());
81 bytes[48..112].copy_from_slice(&self.signature);
82 bytes[112..144].copy_from_slice(&self.public_key);
83 bytes
84 }
85
86 pub fn signed_bytes(&self) -> [u8; Self::SIGNED_BYTES_SIZE] {
88 let mut bytes = [0u8; Self::SIGNED_BYTES_SIZE];
89 bytes[0..4].copy_from_slice(&self.tool_id.to_le_bytes());
90 bytes[4..36].copy_from_slice(&self.schema_hash);
91 bytes[36..44].copy_from_slice(&self.capabilities.to_le_bytes());
92 bytes
93 }
94}
95
96impl SignedInvocation {
97 pub const SIZE: usize = 120; pub const SIGNED_BYTES_SIZE: usize = 52;
102
103 #[inline(always)]
105 pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, DCPError> {
106 let bytes = bytes.as_ref();
107 if bytes.len() < Self::SIZE {
108 return Err(DCPError::InsufficientData);
109 }
110 if bytes.len() != Self::SIZE {
111 return Err(DCPError::ValidationFailed);
112 }
113 if bytes[4..8].iter().any(|&byte| byte != 0) {
114 return Err(DCPError::ValidationFailed);
115 }
116
117 let mut args_hash = [0u8; 32];
118 args_hash.copy_from_slice(&bytes[24..56]);
119 let mut signature = [0u8; 64];
120 signature.copy_from_slice(&bytes[56..120]);
121
122 Ok(Self {
123 tool_id: u32::from_le_bytes(bytes[0..4].try_into().unwrap()),
124 nonce: u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
125 timestamp: u64::from_le_bytes(bytes[16..24].try_into().unwrap()),
126 args_hash,
127 signature,
128 })
129 }
130
131 #[inline(always)]
133 pub fn as_bytes(&self) -> [u8; Self::SIZE] {
134 let mut bytes = [0u8; Self::SIZE];
135 bytes[0..4].copy_from_slice(&self.tool_id.to_le_bytes());
136 bytes[8..16].copy_from_slice(&self.nonce.to_le_bytes());
137 bytes[16..24].copy_from_slice(&self.timestamp.to_le_bytes());
138 bytes[24..56].copy_from_slice(&self.args_hash);
139 bytes[56..120].copy_from_slice(&self.signature);
140 bytes
141 }
142
143 pub fn signed_bytes(&self) -> [u8; Self::SIGNED_BYTES_SIZE] {
145 let mut bytes = [0u8; Self::SIGNED_BYTES_SIZE];
146 bytes[0..4].copy_from_slice(&self.tool_id.to_le_bytes());
147 bytes[4..12].copy_from_slice(&self.nonce.to_le_bytes());
148 bytes[12..20].copy_from_slice(&self.timestamp.to_le_bytes());
149 bytes[20..52].copy_from_slice(&self.args_hash);
150 bytes
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn test_signed_tool_def_size() {
160 assert_eq!(std::mem::size_of::<SignedToolDef>(), SignedToolDef::SIZE);
161 }
162
163 #[test]
164 fn test_signed_invocation_size() {
165 assert_eq!(
166 std::mem::size_of::<SignedInvocation>(),
167 SignedInvocation::SIZE
168 );
169 }
170
171 #[test]
172 fn test_signed_tool_def_round_trip() {
173 let def = SignedToolDef {
174 tool_id: 42,
175 schema_hash: [0xAB; 32],
176 capabilities: 0x1234567890ABCDEF,
177 signature: [0xCD; 64],
178 public_key: [0xEF; 32],
179 };
180 let bytes = def.as_bytes();
181 let parsed = SignedToolDef::from_bytes(bytes).unwrap();
182
183 assert_eq!(parsed.tool_id, 42);
184 assert_eq!(parsed.schema_hash, [0xAB; 32]);
185 assert_eq!(parsed.capabilities, 0x1234567890ABCDEF);
186 assert_eq!(parsed.signature, [0xCD; 64]);
187 assert_eq!(parsed.public_key, [0xEF; 32]);
188 }
189
190 #[test]
191 fn test_signed_invocation_round_trip() {
192 let inv = SignedInvocation {
193 tool_id: 123,
194 nonce: 0xDEADBEEF,
195 timestamp: 1234567890,
196 args_hash: [0x11; 32],
197 signature: [0x22; 64],
198 };
199 let bytes = inv.as_bytes();
200 let parsed = SignedInvocation::from_bytes(bytes).unwrap();
201
202 assert_eq!(parsed.tool_id, 123);
203 assert_eq!(parsed.nonce, 0xDEADBEEF);
204 assert_eq!(parsed.timestamp, 1234567890);
205 assert_eq!(parsed.args_hash, [0x11; 32]);
206 assert_eq!(parsed.signature, [0x22; 64]);
207 }
208
209 #[test]
210 fn test_signed_bytes_length() {
211 let def = SignedToolDef {
212 tool_id: 0,
213 schema_hash: [0; 32],
214 capabilities: 0,
215 signature: [0; 64],
216 public_key: [0; 32],
217 };
218 assert_eq!(def.signed_bytes().len(), 44);
219
220 let inv = SignedInvocation {
221 tool_id: 0,
222 nonce: 0,
223 timestamp: 0,
224 args_hash: [0; 32],
225 signature: [0; 64],
226 };
227 assert_eq!(inv.signed_bytes().len(), 52);
228 }
229}