Skip to main content

calimero_server_primitives/
admin.rs

1use std::collections::BTreeMap;
2
3use calimero_context_config::repr::Repr;
4use calimero_context_config::types::{
5    BlockHeight, Capability, ContextIdentity, ContextStorageEntry, SignedOpenInvitation,
6};
7use calimero_context_config::{Proposal, ProposalWithApprovals};
8use calimero_primitives::alias::Alias;
9use calimero_primitives::application::{Application, ApplicationId};
10use calimero_primitives::context::{Context, ContextId, ContextInvitationPayload};
11use calimero_primitives::hash::Hash;
12use calimero_primitives::identity::{ClientKey, ContextUser, PublicKey, WalletType};
13use camino::Utf8PathBuf;
14use serde::{Deserialize, Serialize};
15use serde_json::Value;
16use url::Url;
17
18#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
19pub struct Empty;
20
21// -------------------------------------------- Application API --------------------------------------------
22#[derive(Clone, Debug, Deserialize, Serialize)]
23#[serde(rename_all = "camelCase")]
24pub struct InstallApplicationRequest {
25    pub url: Url,
26    pub hash: Option<Hash>,
27    pub metadata: Vec<u8>,
28    pub package: Option<String>,
29    pub version: Option<String>,
30}
31
32impl InstallApplicationRequest {
33    pub fn new(
34        url: Url,
35        hash: Option<Hash>,
36        metadata: Vec<u8>,
37        package: Option<String>,
38        version: Option<String>,
39    ) -> Self {
40        Self {
41            url,
42            hash,
43            metadata,
44            package,
45            version,
46        }
47    }
48}
49
50#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
51#[serde(rename_all = "camelCase")]
52pub struct ApplicationInstallResponseData {
53    pub application_id: ApplicationId,
54}
55
56#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
57#[serde(rename_all = "camelCase")]
58pub struct InstallApplicationResponse {
59    pub data: ApplicationInstallResponseData,
60}
61
62impl InstallApplicationResponse {
63    pub const fn new(application_id: ApplicationId) -> Self {
64        Self {
65            data: ApplicationInstallResponseData { application_id },
66        }
67    }
68}
69
70#[derive(Clone, Debug, Deserialize, Serialize)]
71#[serde(rename_all = "camelCase")]
72pub struct InstallDevApplicationRequest {
73    pub path: Utf8PathBuf,
74    pub metadata: Vec<u8>,
75    pub package: Option<String>,
76    pub version: Option<String>,
77}
78
79impl InstallDevApplicationRequest {
80    pub fn new(
81        path: Utf8PathBuf,
82        metadata: Vec<u8>,
83        package: Option<String>,
84        version: Option<String>,
85    ) -> Self {
86        Self {
87            path,
88            metadata,
89            package,
90            version,
91        }
92    }
93}
94
95// -------------------------------------------- Bundle API --------------------------------------------
96#[derive(Clone, Debug, Deserialize, Serialize)]
97#[serde(rename_all = "camelCase")]
98pub struct BundleArtifact {
99    pub path: String,
100    pub hash: Option<String>,
101    pub size: u64,
102}
103
104#[derive(Clone, Debug, Deserialize, Serialize)]
105#[serde(rename_all = "camelCase")]
106pub struct BundleManifest {
107    pub version: String,
108    pub package: String,
109    pub app_version: String,
110    pub wasm: Option<BundleArtifact>,
111    pub abi: Option<BundleArtifact>,
112    pub migrations: Vec<BundleArtifact>,
113}
114
115#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
116#[serde(rename_all = "camelCase")]
117pub struct UninstallApplicationResponseData {
118    pub application_id: ApplicationId,
119}
120
121#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
122#[serde(rename_all = "camelCase")]
123pub struct UninstallApplicationResponse {
124    pub data: UninstallApplicationResponseData,
125}
126
127impl UninstallApplicationResponse {
128    pub const fn new(application_id: ApplicationId) -> Self {
129        Self {
130            data: UninstallApplicationResponseData { application_id },
131        }
132    }
133}
134
135// -------------------------------------------- Package Management API --------------------------------------------
136#[derive(Clone, Debug, Deserialize, Serialize)]
137#[serde(rename_all = "camelCase")]
138pub struct ListPackagesResponse {
139    pub packages: Vec<String>,
140}
141
142impl ListPackagesResponse {
143    pub const fn new(packages: Vec<String>) -> Self {
144        Self { packages }
145    }
146}
147
148#[derive(Clone, Debug, Deserialize, Serialize)]
149#[serde(rename_all = "camelCase")]
150pub struct ListVersionsResponse {
151    pub versions: Vec<String>,
152}
153
154impl ListVersionsResponse {
155    pub const fn new(versions: Vec<String>) -> Self {
156        Self { versions }
157    }
158}
159
160#[derive(Clone, Debug, Deserialize, Serialize)]
161#[serde(rename_all = "camelCase")]
162pub struct GetLatestVersionResponse {
163    pub application_id: Option<ApplicationId>,
164    /// Version string of the latest release (e.g. "1.0.0")
165    pub version: Option<String>,
166}
167
168impl GetLatestVersionResponse {
169    pub const fn new(application_id: Option<ApplicationId>, version: Option<String>) -> Self {
170        Self {
171            application_id,
172            version,
173        }
174    }
175}
176
177#[derive(Clone, Debug, Deserialize, Serialize)]
178#[serde(rename_all = "camelCase")]
179pub struct ListApplicationResponseData {
180    pub apps: Vec<Application>,
181}
182
183#[derive(Debug, Deserialize, Serialize)]
184#[serde(rename_all = "camelCase")]
185pub struct ListApplicationsResponse {
186    pub data: ListApplicationResponseData,
187}
188
189impl ListApplicationsResponse {
190    pub const fn new(apps: Vec<Application>) -> Self {
191        Self {
192            data: ListApplicationResponseData { apps },
193        }
194    }
195}
196
197#[derive(Debug, Deserialize, Serialize)]
198#[serde(rename_all = "camelCase")]
199pub struct GetApplicationResponseData {
200    pub application: Option<Application>,
201}
202
203#[derive(Debug, Deserialize, Serialize)]
204#[serde(rename_all = "camelCase")]
205pub struct GetApplicationResponse {
206    pub data: GetApplicationResponseData,
207}
208
209impl GetApplicationResponse {
210    pub const fn new(application: Option<Application>) -> Self {
211        Self {
212            data: GetApplicationResponseData { application },
213        }
214    }
215}
216// -------------------------------------------- Context API --------------------------------------------
217#[derive(Clone, Debug, Deserialize, Serialize)]
218#[serde(rename_all = "camelCase")]
219pub struct CreateContextRequest {
220    pub protocol: String,
221    pub application_id: ApplicationId,
222    pub context_seed: Option<Hash>,
223    pub initialization_params: Vec<u8>,
224}
225
226impl CreateContextRequest {
227    pub const fn new(
228        protocol: String,
229        application_id: ApplicationId,
230        context_seed: Option<Hash>,
231        initialization_params: Vec<u8>,
232    ) -> Self {
233        Self {
234            protocol,
235            application_id,
236            context_seed,
237            initialization_params,
238        }
239    }
240}
241
242#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
243#[serde(rename_all = "camelCase")]
244pub struct CreateContextResponseData {
245    pub context_id: ContextId,
246    pub member_public_key: PublicKey,
247}
248
249#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
250#[serde(rename_all = "camelCase")]
251pub struct CreateContextResponse {
252    pub data: CreateContextResponseData,
253}
254
255impl CreateContextResponse {
256    pub const fn new(context_id: ContextId, member_public_key: PublicKey) -> Self {
257        Self {
258            data: CreateContextResponseData {
259                context_id,
260                member_public_key,
261            },
262        }
263    }
264}
265
266#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
267#[serde(rename_all = "camelCase")]
268pub struct DeletedContextResponseData {
269    pub is_deleted: bool,
270}
271
272#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
273#[serde(rename_all = "camelCase")]
274pub struct DeleteContextResponse {
275    pub data: DeletedContextResponseData,
276}
277
278impl DeleteContextResponse {
279    pub const fn new(is_deleted: bool) -> Self {
280        Self {
281            data: DeletedContextResponseData { is_deleted },
282        }
283    }
284}
285
286#[derive(Clone, Debug, Deserialize, Serialize)]
287#[serde(rename_all = "camelCase")]
288pub struct GetContextResponse {
289    pub data: Context,
290}
291
292#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
293#[serde(rename_all = "camelCase")]
294pub struct GetContextStorageResponseData {
295    pub size_in_bytes: u64,
296}
297
298#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
299#[serde(rename_all = "camelCase")]
300pub struct GetContextStorageResponse {
301    pub data: GetContextStorageResponseData,
302}
303
304impl GetContextStorageResponse {
305    pub const fn new(size_in_bytes: u64) -> Self {
306        Self {
307            data: GetContextStorageResponseData { size_in_bytes },
308        }
309    }
310}
311
312#[derive(Clone, Debug, Deserialize, Serialize)]
313#[serde(rename_all = "camelCase")]
314pub struct ContextIdentitiesResponseData {
315    pub identities: Vec<PublicKey>,
316}
317
318#[derive(Clone, Debug, Deserialize, Serialize)]
319#[serde(rename_all = "camelCase")]
320pub struct GetContextIdentitiesResponse {
321    pub data: ContextIdentitiesResponseData,
322}
323
324impl GetContextIdentitiesResponse {
325    pub const fn new(identities: Vec<PublicKey>) -> Self {
326        Self {
327            data: ContextIdentitiesResponseData { identities },
328        }
329    }
330}
331
332#[derive(Clone, Debug, Serialize, Deserialize)]
333#[serde(rename_all = "camelCase")]
334pub struct ListAliasesResponse<T> {
335    #[serde(bound(deserialize = "T: Ord + Deserialize<'de>"))]
336    pub data: BTreeMap<Alias<T>, T>,
337}
338
339impl<T> ListAliasesResponse<T> {
340    pub fn new(data: BTreeMap<Alias<T>, T>) -> Self {
341        Self { data }
342    }
343}
344
345#[derive(Clone, Debug, Deserialize, Serialize)]
346#[serde(rename_all = "camelCase")]
347pub struct GetContextClientKeysResponseData {
348    pub client_keys: Vec<ClientKey>,
349}
350
351#[derive(Clone, Debug, Deserialize, Serialize)]
352#[serde(rename_all = "camelCase")]
353pub struct GetContextClientKeysResponse {
354    pub data: GetContextClientKeysResponseData,
355}
356
357impl GetContextClientKeysResponse {
358    pub const fn new(client_keys: Vec<ClientKey>) -> Self {
359        Self {
360            data: GetContextClientKeysResponseData { client_keys },
361        }
362    }
363}
364
365#[derive(Debug, Deserialize, Serialize)]
366#[serde(rename_all = "camelCase")]
367pub struct GetContextUsersResponseData {
368    pub context_users: Vec<ContextUser>,
369}
370
371#[derive(Debug, Deserialize, Serialize)]
372#[serde(rename_all = "camelCase")]
373pub struct GetContextUsersResponse {
374    pub data: GetContextUsersResponseData,
375}
376
377impl GetContextUsersResponse {
378    pub const fn new(context_users: Vec<ContextUser>) -> Self {
379        Self {
380            data: GetContextUsersResponseData { context_users },
381        }
382    }
383}
384
385#[derive(Debug, Deserialize, Serialize)]
386#[serde(rename_all = "camelCase")]
387pub struct GetContextsResponseData {
388    pub contexts: Vec<Context>,
389}
390
391#[derive(Debug, Deserialize, Serialize)]
392#[serde(rename_all = "camelCase")]
393pub struct GetContextsResponse {
394    pub data: GetContextsResponseData,
395}
396
397impl GetContextsResponse {
398    pub const fn new(contexts: Vec<Context>) -> Self {
399        Self {
400            data: GetContextsResponseData { contexts },
401        }
402    }
403}
404
405#[derive(Debug, Deserialize, Serialize)]
406#[serde(rename_all = "camelCase")]
407pub struct InviteToContextRequest {
408    pub context_id: ContextId,
409    pub inviter_id: PublicKey,
410    pub invitee_id: PublicKey,
411}
412
413impl InviteToContextRequest {
414    pub const fn new(context_id: ContextId, inviter_id: PublicKey, invitee_id: PublicKey) -> Self {
415        Self {
416            context_id,
417            inviter_id,
418            invitee_id,
419        }
420    }
421}
422
423#[derive(Debug, Deserialize, Serialize)]
424#[serde(rename_all = "camelCase")]
425pub struct InviteToContextResponse {
426    pub data: Option<ContextInvitationPayload>,
427}
428
429impl InviteToContextResponse {
430    pub const fn new(payload: Option<ContextInvitationPayload>) -> Self {
431        Self { data: payload }
432    }
433}
434
435// TODO: refactor `InviteToContextRequest` with an optional `invitee_id` field to serve both
436// types of requests.
437#[derive(Debug, Deserialize, Copy, Clone, Serialize)]
438#[serde(rename_all = "camelCase")]
439pub struct InviteToContextOpenInvitationRequest {
440    pub context_id: ContextId,
441    pub inviter_id: PublicKey,
442    pub valid_for_blocks: BlockHeight,
443}
444
445impl InviteToContextOpenInvitationRequest {
446    pub const fn new(
447        context_id: ContextId,
448        inviter_id: PublicKey,
449        valid_for_blocks: BlockHeight,
450    ) -> Self {
451        Self {
452            context_id,
453            inviter_id,
454            valid_for_blocks,
455        }
456    }
457}
458
459#[derive(Debug, Deserialize, Serialize)]
460#[serde(rename_all = "camelCase")]
461pub struct InviteToContextOpenInvitationResponse {
462    pub data: Option<SignedOpenInvitation>,
463}
464
465impl InviteToContextOpenInvitationResponse {
466    pub const fn new(signed_open_invitation: Option<SignedOpenInvitation>) -> Self {
467        Self {
468            data: signed_open_invitation,
469        }
470    }
471}
472
473/// Request to invite specialized nodes (e.g., read-only TEE nodes) to join a context
474#[derive(Clone, Debug, Deserialize, Serialize)]
475#[serde(rename_all = "camelCase")]
476pub struct InviteSpecializedNodeRequest {
477    pub context_id: ContextId,
478    /// Optional inviter identity - defaults to context's default identity if not provided
479    pub inviter_id: Option<PublicKey>,
480}
481
482impl InviteSpecializedNodeRequest {
483    pub const fn new(context_id: ContextId, inviter_id: Option<PublicKey>) -> Self {
484        Self {
485            context_id,
486            inviter_id,
487        }
488    }
489}
490
491#[derive(Clone, Debug, Deserialize, Serialize)]
492#[serde(rename_all = "camelCase")]
493pub struct InviteSpecializedNodeResponseData {
494    /// Hex-encoded nonce used for the specialized node invite discovery
495    pub nonce: String,
496}
497
498#[derive(Clone, Debug, Deserialize, Serialize)]
499#[serde(rename_all = "camelCase")]
500pub struct InviteSpecializedNodeResponse {
501    pub data: InviteSpecializedNodeResponseData,
502}
503
504impl InviteSpecializedNodeResponse {
505    pub fn new(nonce: String) -> Self {
506        Self {
507            data: InviteSpecializedNodeResponseData { nonce },
508        }
509    }
510}
511
512#[derive(Clone, Debug, Serialize, Deserialize)]
513#[serde(rename_all = "camelCase")]
514pub struct JoinContextRequest {
515    pub invitation_payload: ContextInvitationPayload,
516}
517
518impl JoinContextRequest {
519    pub const fn new(invitation_payload: ContextInvitationPayload) -> Self {
520        Self { invitation_payload }
521    }
522}
523
524#[derive(Clone, Debug, Serialize, Deserialize)]
525#[serde(rename_all = "camelCase")]
526pub struct JoinContextByOpenInvitationRequest {
527    pub invitation: SignedOpenInvitation,
528    pub new_member_public_key: PublicKey,
529}
530
531impl JoinContextByOpenInvitationRequest {
532    pub const fn new(invitation: SignedOpenInvitation, new_member_public_key: PublicKey) -> Self {
533        Self {
534            invitation,
535            new_member_public_key,
536        }
537    }
538}
539
540#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
541#[serde(rename_all = "camelCase")]
542pub struct JoinContextResponseData {
543    pub context_id: ContextId,
544    pub member_public_key: PublicKey,
545}
546
547#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
548#[serde(rename_all = "camelCase")]
549pub struct JoinContextResponse {
550    pub data: Option<JoinContextResponseData>,
551}
552
553impl JoinContextResponse {
554    pub fn new(data: Option<(ContextId, PublicKey)>) -> Self {
555        Self {
556            data: data.map(|(context_id, member_public_key)| JoinContextResponseData {
557                context_id,
558                member_public_key,
559            }),
560        }
561    }
562}
563
564#[derive(Clone, Debug, Deserialize, Serialize)]
565#[serde(rename_all = "camelCase")]
566pub struct UpdateContextApplicationRequest {
567    pub application_id: ApplicationId,
568    pub executor_public_key: PublicKey,
569    /// Optional migration function name to execute during the update.
570    /// The function must be decorated with `#[app::migrate]` in the new application.
571    #[serde(skip_serializing_if = "Option::is_none")]
572    pub migrate_method: Option<String>,
573}
574
575impl UpdateContextApplicationRequest {
576    pub const fn new(application_id: ApplicationId, executor_public_key: PublicKey) -> Self {
577        Self {
578            application_id,
579            executor_public_key,
580            migrate_method: None,
581        }
582    }
583
584    pub fn with_migration(
585        application_id: ApplicationId,
586        executor_public_key: PublicKey,
587        migrate_method: String,
588    ) -> Self {
589        Self {
590            application_id,
591            executor_public_key,
592            migrate_method: Some(migrate_method),
593        }
594    }
595}
596
597#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
598#[serde(rename_all = "camelCase")]
599pub struct UpdateContextApplicationResponse {
600    pub data: Empty,
601}
602
603impl UpdateContextApplicationResponse {
604    pub const fn new() -> Self {
605        Self { data: Empty {} }
606    }
607}
608
609// -------------------------------------------- Identity API ----------------------------------------
610#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
611#[serde(rename_all = "camelCase")]
612pub struct GenerateContextIdentityResponseData {
613    pub public_key: PublicKey,
614}
615
616#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
617#[serde(rename_all = "camelCase")]
618pub struct GenerateContextIdentityResponse {
619    pub data: GenerateContextIdentityResponseData,
620}
621
622impl GenerateContextIdentityResponse {
623    pub const fn new(public_key: PublicKey) -> Self {
624        Self {
625            data: GenerateContextIdentityResponseData { public_key },
626        }
627    }
628}
629
630#[derive(Clone, Debug, Deserialize, Serialize)]
631pub struct CreateAliasRequest<T: AliasKind> {
632    pub alias: Alias<T>,
633    #[serde(flatten)]
634    pub value: T::Value,
635}
636
637pub trait AliasKind {
638    type Value;
639
640    fn from_value(data: Self::Value) -> Self;
641}
642
643#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
644#[serde(rename_all = "camelCase")]
645pub struct CreateContextIdAlias {
646    pub context_id: ContextId,
647}
648
649impl AliasKind for ContextId {
650    type Value = CreateContextIdAlias;
651
652    fn from_value(data: Self::Value) -> Self {
653        data.context_id
654    }
655}
656
657#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
658pub struct CreateContextIdentityAlias {
659    pub identity: PublicKey,
660}
661
662impl AliasKind for PublicKey {
663    type Value = CreateContextIdentityAlias;
664
665    fn from_value(data: Self::Value) -> Self {
666        data.identity
667    }
668}
669
670#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
671#[serde(rename_all = "camelCase")]
672pub struct CreateApplicationIdAlias {
673    pub application_id: ApplicationId,
674}
675
676impl AliasKind for ApplicationId {
677    type Value = CreateApplicationIdAlias;
678
679    fn from_value(data: Self::Value) -> Self {
680        data.application_id
681    }
682}
683
684#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
685#[serde(rename_all = "camelCase")]
686pub struct CreateAliasResponse {
687    pub data: Empty,
688}
689
690impl CreateAliasResponse {
691    pub const fn new() -> Self {
692        Self { data: Empty {} }
693    }
694}
695
696#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
697#[serde(rename_all = "camelCase")]
698pub struct DeleteAliasResponse {
699    pub data: Empty,
700}
701
702impl DeleteAliasResponse {
703    pub const fn new() -> Self {
704        Self { data: Empty {} }
705    }
706}
707
708#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
709#[serde(rename_all = "camelCase")]
710pub struct LookupAliasResponse<T> {
711    pub data: LookupAliasResponseData<T>,
712}
713
714#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
715#[serde(rename_all = "camelCase")]
716pub struct LookupAliasResponseData<T> {
717    pub value: Option<T>,
718}
719
720impl<T> LookupAliasResponseData<T> {
721    pub const fn new(value: Option<T>) -> Self {
722        Self { value }
723    }
724}
725
726// -------------------------------------------- Misc API --------------------------------------------
727
728#[derive(Debug, Serialize, Deserialize)]
729#[serde(rename_all = "camelCase")]
730#[non_exhaustive]
731pub struct GetPeersCountResponse {
732    pub count: usize,
733}
734
735impl GetPeersCountResponse {
736    #[must_use]
737    pub fn new(count: usize) -> Self {
738        Self { count }
739    }
740}
741
742#[derive(Debug, Deserialize)]
743#[serde(rename_all = "camelCase")]
744#[non_exhaustive]
745pub struct AddPublicKeyRequest {
746    pub wallet_signature: WalletSignature,
747    pub payload: Payload,
748    pub wallet_metadata: WalletMetadata,
749    pub context_id: Option<ContextId>,
750}
751
752impl AddPublicKeyRequest {
753    #[must_use]
754    pub const fn new(
755        wallet_signature: WalletSignature,
756        payload: Payload,
757        wallet_metadata: WalletMetadata,
758        context_id: Option<ContextId>,
759    ) -> Self {
760        Self {
761            wallet_signature,
762            payload,
763            wallet_metadata,
764            context_id,
765        }
766    }
767}
768
769#[derive(Debug, Deserialize)]
770#[serde(rename_all = "camelCase")]
771#[non_exhaustive]
772pub struct Payload {
773    pub message: SignatureMessage,
774    pub metadata: SignatureMetadataEnum,
775}
776
777impl Payload {
778    #[must_use]
779    pub const fn new(message: SignatureMessage, metadata: SignatureMetadataEnum) -> Self {
780        Self { message, metadata }
781    }
782}
783
784#[derive(Debug, Deserialize)]
785#[serde(rename_all = "camelCase")]
786#[non_exhaustive]
787pub struct SignatureMessage {
788    pub context_id: Option<ContextId>,
789    pub nonce: String,
790    pub timestamp: i64,
791    pub node_signature: String,
792    pub message: String,
793    pub public_key: String,
794}
795
796#[derive(Debug, Deserialize)]
797#[serde(rename_all = "camelCase")]
798#[non_exhaustive]
799pub struct WalletMetadata {
800    #[serde(rename = "wallet")]
801    pub wallet_type: WalletType,
802    pub verifying_key: String,
803    pub wallet_address: Option<String>,
804}
805
806#[derive(Debug, Deserialize)]
807#[serde(tag = "type", content = "data")]
808#[non_exhaustive]
809pub enum SignatureMetadataEnum {
810    NEAR(NearSignatureMessageMetadata),
811}
812
813#[derive(Debug, Deserialize)]
814#[serde(rename_all = "camelCase")]
815#[non_exhaustive]
816pub struct NearSignatureMessageMetadata {
817    pub recipient: String,
818    pub callback_url: String,
819    pub nonce: String,
820}
821
822// Intermediate structs for initial parsing
823#[derive(Debug, Deserialize)]
824#[serde(rename_all = "camelCase")]
825#[non_exhaustive]
826pub struct IntermediateAddPublicKeyRequest {
827    pub wallet_signature: WalletSignature,
828    pub payload: IntermediatePayload,
829    pub wallet_metadata: WalletMetadata, // Reuse WalletMetadata as it fits the intermediate step
830    pub context_id: Option<ContextId>,
831}
832
833#[derive(Debug, Deserialize)]
834#[serde(untagged)]
835#[non_exhaustive]
836pub enum WalletSignature {
837    String(String),
838}
839
840#[derive(Debug, Deserialize)]
841#[serde(rename_all = "camelCase")]
842#[non_exhaustive]
843pub struct JwtTokenRequest {
844    pub context_id: ContextId,
845    pub executor_public_key: String,
846}
847
848impl JwtTokenRequest {
849    #[must_use]
850    pub const fn new(context_id: ContextId, executor_public_key: String) -> Self {
851        Self {
852            context_id,
853            executor_public_key,
854        }
855    }
856}
857
858#[derive(Debug, Deserialize)]
859#[serde(rename_all = "camelCase")]
860#[non_exhaustive]
861pub struct JwtRefreshRequest {
862    pub refresh_token: String,
863}
864
865#[derive(Debug, Deserialize)]
866#[serde(rename_all = "camelCase")]
867#[non_exhaustive]
868pub struct IntermediatePayload {
869    pub message: SignatureMessage, // Reuse SignatureMessage as it fits the intermediate step
870    pub metadata: Value,           // Raw JSON value for the metadata
871}
872
873#[derive(Debug, Deserialize, Serialize)]
874#[serde(rename_all = "camelCase")]
875#[non_exhaustive]
876pub struct NodeChallenge {
877    #[serde(flatten)]
878    pub message: NodeChallengeMessage,
879    pub node_signature: String,
880}
881
882impl NodeChallenge {
883    #[must_use]
884    pub const fn new(message: NodeChallengeMessage, node_signature: String) -> Self {
885        Self {
886            message,
887            node_signature,
888        }
889    }
890}
891
892#[derive(Debug, Deserialize, Serialize)]
893#[serde(rename_all = "camelCase")]
894#[non_exhaustive]
895pub struct NodeChallengeMessage {
896    pub nonce: String,
897    pub context_id: Option<ContextId>,
898    pub timestamp: i64,
899}
900
901impl NodeChallengeMessage {
902    #[must_use]
903    pub const fn new(nonce: String, context_id: Option<ContextId>, timestamp: i64) -> Self {
904        Self {
905            nonce,
906            context_id,
907            timestamp,
908        }
909    }
910}
911
912#[derive(Debug, Deserialize, Serialize)]
913#[serde(rename_all = "camelCase")]
914pub struct GetProposalsResponse {
915    pub data: Vec<Proposal>,
916}
917
918#[derive(Debug, Deserialize, Serialize)]
919#[serde(rename_all = "camelCase")]
920pub struct GetProposalResponse {
921    pub data: Proposal,
922}
923
924#[derive(Debug, Deserialize, Serialize)]
925#[serde(rename_all = "camelCase")]
926pub struct GetProxyContractResponse {
927    pub data: String,
928}
929
930#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
931#[serde(rename_all = "camelCase")]
932pub struct GetProposalsRequest {
933    pub offset: usize,
934    pub limit: usize,
935}
936
937#[derive(Debug, Deserialize, Serialize)]
938#[serde(rename_all = "camelCase")]
939pub struct GetContextValueRequest {
940    pub key: String,
941}
942
943#[derive(Debug, Deserialize, Serialize, Copy, Clone)]
944#[serde(rename_all = "camelCase")]
945pub struct GetContextStorageEntriesRequest {
946    pub offset: usize,
947    pub limit: usize,
948}
949
950#[derive(Debug, Deserialize, Serialize)]
951#[serde(rename_all = "camelCase")]
952pub struct GetContextValueResponse {
953    pub data: Vec<u8>,
954}
955
956#[derive(Debug, Deserialize, Serialize)]
957#[serde(rename_all = "camelCase")]
958pub struct GetContextStorageEntriesResponse {
959    pub data: Vec<ContextStorageEntry>,
960}
961
962#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
963#[serde(rename_all = "camelCase")]
964pub struct GetNumberOfActiveProposalsResponse {
965    pub data: u16,
966}
967
968#[derive(Debug, Deserialize, Serialize)]
969#[serde(rename_all = "camelCase")]
970pub struct GetProposalApproversResponse {
971    // fixme! this is wrong, ContextIdentity is an implementation
972    // fixme! detail it should be PublicKey instead
973    pub data: Vec<Repr<ContextIdentity>>,
974}
975
976#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
977#[serde(rename_all = "camelCase")]
978pub struct GetNumberOfProposalApprovalsResponse {
979    pub data: ProposalWithApprovals,
980}
981
982#[derive(Debug, Deserialize, Serialize)]
983#[serde(rename_all = "camelCase")]
984pub struct GrantPermissionRequest {
985    pub context_id: ContextId,
986    pub granter_id: PublicKey,
987    pub grantee_id: PublicKey,
988    pub capability: Capability,
989}
990
991impl GrantPermissionRequest {
992    pub const fn new(
993        context_id: ContextId,
994        granter_id: PublicKey,
995        grantee_id: PublicKey,
996        capability: Capability,
997    ) -> Self {
998        Self {
999            context_id,
1000            granter_id,
1001            grantee_id,
1002            capability,
1003        }
1004    }
1005}
1006
1007#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
1008#[serde(rename_all = "camelCase")]
1009pub struct GrantPermissionResponse {
1010    pub data: Empty,
1011}
1012
1013impl GrantPermissionResponse {
1014    pub const fn new() -> Self {
1015        Self { data: Empty {} }
1016    }
1017}
1018
1019#[derive(Debug, Deserialize, Serialize)]
1020#[serde(rename_all = "camelCase")]
1021pub struct RevokePermissionRequest {
1022    pub context_id: ContextId,
1023    pub revoker_id: PublicKey,
1024    pub revokee_id: PublicKey,
1025    pub capability: Capability,
1026}
1027
1028impl RevokePermissionRequest {
1029    pub const fn new(
1030        context_id: ContextId,
1031        revoker_id: PublicKey,
1032        revokee_id: PublicKey,
1033        capability: Capability,
1034    ) -> Self {
1035        Self {
1036            context_id,
1037            revoker_id,
1038            revokee_id,
1039            capability,
1040        }
1041    }
1042}
1043
1044#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
1045#[serde(rename_all = "camelCase")]
1046pub struct RevokePermissionResponse {
1047    pub data: Empty,
1048}
1049
1050impl RevokePermissionResponse {
1051    pub const fn new() -> Self {
1052        Self { data: Empty {} }
1053    }
1054}
1055
1056#[derive(Clone, Debug, Deserialize, Serialize)]
1057#[serde(rename_all = "camelCase")]
1058pub struct CreateAndApproveProposalRequest {
1059    pub signer_id: PublicKey,
1060    pub proposal: Proposal,
1061}
1062
1063#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
1064#[serde(rename_all = "camelCase")]
1065pub struct CreateAndApproveProposalResponse {
1066    pub data: Option<ProposalWithApprovals>,
1067}
1068
1069impl CreateAndApproveProposalResponse {
1070    pub const fn new(data: Option<ProposalWithApprovals>) -> Self {
1071        Self { data }
1072    }
1073}
1074
1075#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
1076#[serde(rename_all = "camelCase")]
1077pub struct ApproveProposalRequest {
1078    pub signer_id: PublicKey,
1079    pub proposal_id: calimero_context_config::types::ProposalId,
1080}
1081
1082#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
1083#[serde(rename_all = "camelCase")]
1084pub struct ApproveProposalResponse {
1085    pub data: Option<ProposalWithApprovals>,
1086}
1087
1088impl ApproveProposalResponse {
1089    pub const fn new(data: Option<ProposalWithApprovals>) -> Self {
1090        Self { data }
1091    }
1092}
1093
1094#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
1095#[serde(rename_all = "camelCase")]
1096pub struct SyncContextResponse {
1097    pub data: Empty,
1098}
1099
1100impl SyncContextResponse {
1101    pub const fn new() -> Self {
1102        Self { data: Empty {} }
1103    }
1104}
1105
1106// -------------------------------------------- TEE API --------------------------------------------
1107
1108// Serializable TDX Quote Types (mirrors tdx_quote::Quote structure)
1109
1110#[derive(Debug, Clone, Serialize, Deserialize)]
1111#[serde(rename_all = "camelCase")]
1112pub struct Quote {
1113    pub header: QuoteHeader,
1114    pub body: QuoteBody,
1115    pub signature: String,
1116    pub attestation_key: String,
1117    pub certification_data: CertificationData,
1118}
1119
1120#[derive(Debug, Clone, Serialize, Deserialize)]
1121#[serde(rename_all = "camelCase")]
1122pub struct QuoteHeader {
1123    pub version: u16,
1124    pub attestation_key_type: u16,
1125    pub tee_type: u32,
1126    pub qe_vendor_id: String,
1127    pub user_data: String,
1128}
1129
1130#[derive(Debug, Clone, Serialize, Deserialize)]
1131#[serde(rename_all = "camelCase")]
1132pub struct QuoteBody {
1133    /// TDX version
1134    pub tdx_version: String,
1135    /// TEE Trusted Computing Base Security Version Number (16 bytes)
1136    pub tee_tcb_svn: String,
1137    /// Measurement of SEAM module (48 bytes)
1138    pub mrseam: String,
1139    /// Measurement of SEAM signer (48 bytes)
1140    pub mrsignerseam: String,
1141    /// SEAM attributes (8 bytes)
1142    pub seamattributes: String,
1143    /// Trust Domain attributes (8 bytes)
1144    pub tdattributes: String,
1145    /// Extended features available mask (8 bytes)
1146    pub xfam: String,
1147    /// Measurement Register of Trust Domain (48 bytes) - hash of kernel + initrd + app
1148    pub mrtd: String,
1149    /// Measurement of configuration (48 bytes)
1150    pub mrconfigid: String,
1151    /// Measurement of owner (48 bytes)
1152    pub mrowner: String,
1153    /// Measurement of owner configuration (48 bytes)
1154    pub mrownerconfig: String,
1155    /// Runtime Measurement Register 0 (48 bytes)
1156    pub rtmr0: String,
1157    /// Runtime Measurement Register 1 (48 bytes)
1158    pub rtmr1: String,
1159    /// Runtime Measurement Register 2 (48 bytes)
1160    pub rtmr2: String,
1161    /// Runtime Measurement Register 3 (48 bytes)
1162    pub rtmr3: String,
1163    /// Report data (64 bytes): nonce[32] || app_hash[32]
1164    pub reportdata: String,
1165    /// Optional second TEE TCB SVN (16 bytes) - TDX 1.5+
1166    #[serde(skip_serializing_if = "Option::is_none")]
1167    pub tee_tcb_svn_2: Option<String>,
1168    /// Optional measurement of service TD (48 bytes) - TDX 1.5+
1169    #[serde(skip_serializing_if = "Option::is_none")]
1170    pub mrservicetd: Option<String>,
1171}
1172
1173#[derive(Debug, Clone, Serialize, Deserialize)]
1174#[serde(rename_all = "camelCase")]
1175pub struct QeReportCertificationDataInfo {
1176    /// QE report (384 bytes hex)
1177    pub qe_report: String,
1178    /// ECDSA signature (hex)
1179    pub signature: String,
1180    /// QE authentication data (hex)
1181    pub qe_authentication_data: String,
1182    /// Inner certification data type
1183    pub certification_data_type: String,
1184    /// Inner certification data (hex)
1185    pub certification_data: String,
1186}
1187
1188#[derive(Debug, Clone, Serialize, Deserialize)]
1189#[serde(tag = "type", content = "data", rename_all = "camelCase")]
1190pub enum CertificationData {
1191    #[serde(rename = "pckIdPpidPlainCpusvnPcesvn")]
1192    PckIdPpidPlainCpusvnPcesvn(String),
1193    #[serde(rename = "pckIdPpidRSA2048CpusvnPcesvn")]
1194    PckIdPpidRSA2048CpusvnPcesvn(String),
1195    #[serde(rename = "pckIdPpidRSA3072CpusvnPcesvn")]
1196    PckIdPpidRSA3072CpusvnPcesvn(String),
1197    #[serde(rename = "pckLeafCert")]
1198    PckLeafCert(String),
1199    #[serde(rename = "pckCertChain")]
1200    PckCertChain(String),
1201    #[serde(rename = "qeReportCertificationData")]
1202    QeReportCertificationData(QeReportCertificationDataInfo),
1203    #[serde(rename = "platformManifest")]
1204    PlatformManifest(String),
1205}
1206
1207// Conversion from tdx_quote::Quote to our serializable Quote type
1208impl TryFrom<tdx_quote::Quote> for Quote {
1209    type Error = String;
1210
1211    fn try_from(quote: tdx_quote::Quote) -> Result<Self, Self::Error> {
1212        use tdx_quote::CertificationData as TdxCert;
1213        use tdx_quote::CertificationDataInner;
1214
1215        // Extract method results first to avoid borrow issues
1216        let mrtd = hex::encode(quote.mrtd());
1217        let rtmr0 = hex::encode(quote.rtmr0());
1218        let rtmr1 = hex::encode(quote.rtmr1());
1219        let rtmr2 = hex::encode(quote.rtmr2());
1220        let rtmr3 = hex::encode(quote.rtmr3());
1221        let reportdata = hex::encode(quote.report_input_data());
1222
1223        Ok(Self {
1224            header: QuoteHeader {
1225                version: quote.header.version,
1226                attestation_key_type: quote.header.attestation_key_type as u16,
1227                tee_type: quote.header.tee_type as u32,
1228                qe_vendor_id: hex::encode(&quote.header.qe_vendor_id),
1229                user_data: hex::encode(&quote.header.user_data),
1230            },
1231            body: QuoteBody {
1232                tdx_version: match quote.body.tdx_version {
1233                    tdx_quote::TDXVersion::One => "1.0".to_string(),
1234                    tdx_quote::TDXVersion::OnePointFive => "1.5".to_string(),
1235                },
1236                tee_tcb_svn: hex::encode(&quote.body.tee_tcb_svn),
1237                mrseam: hex::encode(&quote.body.mrseam),
1238                mrsignerseam: hex::encode(&quote.body.mrsignerseam),
1239                seamattributes: hex::encode(&quote.body.seamattributes),
1240                tdattributes: hex::encode(&quote.body.tdattributes),
1241                xfam: hex::encode(&quote.body.xfam),
1242                mrtd,
1243                mrconfigid: hex::encode(&quote.body.mrconfigid),
1244                mrowner: hex::encode(&quote.body.mrowner),
1245                mrownerconfig: hex::encode(&quote.body.mrownerconfig),
1246                rtmr0,
1247                rtmr1,
1248                rtmr2,
1249                rtmr3,
1250                reportdata,
1251                tee_tcb_svn_2: quote.body.tee_tcb_svn_2.map(|v| hex::encode(&v)),
1252                mrservicetd: quote.body.mrservicetd.map(|v| hex::encode(&v)),
1253            },
1254            signature: hex::encode(quote.signature.to_bytes()),
1255            attestation_key: hex::encode(quote.attestation_key.to_sec1_bytes()),
1256            certification_data: match quote.certification_data {
1257                TdxCert::PckIdPpidPlainCpusvnPcesvn(data) => {
1258                    CertificationData::PckIdPpidPlainCpusvnPcesvn(hex::encode(&data))
1259                }
1260                TdxCert::PckIdPpidRSA2048CpusvnPcesvn(data) => {
1261                    CertificationData::PckIdPpidRSA2048CpusvnPcesvn(hex::encode(&data))
1262                }
1263                TdxCert::PckIdPpidRSA3072CpusvnPcesvn(data) => {
1264                    CertificationData::PckIdPpidRSA3072CpusvnPcesvn(hex::encode(&data))
1265                }
1266                TdxCert::PckLeafCert(data) => CertificationData::PckLeafCert(hex::encode(&data)),
1267                TdxCert::PckCertChain(data) => CertificationData::PckCertChain(hex::encode(&data)),
1268                TdxCert::QeReportCertificationData(data) => {
1269                    // Properly serialize the nested QeReportCertificationData structure
1270                    let (cert_type, cert_data) = match &data.certification_data {
1271                        CertificationDataInner::PckIdPpidPlainCpusvnPcesvn(d) => {
1272                            ("PckIdPpidPlainCpusvnPcesvn", hex::encode(d))
1273                        }
1274                        CertificationDataInner::PckIdPpidRSA2048CpusvnPcesvn(d) => {
1275                            ("PckIdPpidRSA2048CpusvnPcesvn", hex::encode(d))
1276                        }
1277                        CertificationDataInner::PckIdPpidRSA3072CpusvnPcesvn(d) => {
1278                            ("PckIdPpidRSA3072CpusvnPcesvn", hex::encode(d))
1279                        }
1280                        CertificationDataInner::PckLeafCert(d) => ("PckLeafCert", hex::encode(d)),
1281                        CertificationDataInner::PckCertChain(d) => ("PckCertChain", hex::encode(d)),
1282                        CertificationDataInner::PlatformManifest(d) => {
1283                            ("PlatformManifest", hex::encode(d))
1284                        }
1285                        // Return error for unknown inner certification data variants
1286                        _ => {
1287                            return Err(
1288                                "Unknown CertificationDataInner variant encountered".to_string()
1289                            )
1290                        }
1291                    };
1292
1293                    CertificationData::QeReportCertificationData(QeReportCertificationDataInfo {
1294                        qe_report: hex::encode(&data.qe_report),
1295                        signature: hex::encode(data.signature.to_bytes()),
1296                        qe_authentication_data: hex::encode(&data.qe_authentication_data),
1297                        certification_data_type: cert_type.to_string(),
1298                        certification_data: cert_data,
1299                    })
1300                }
1301                TdxCert::PlatformManifest(data) => {
1302                    CertificationData::PlatformManifest(hex::encode(&data))
1303                }
1304                // Return error for unknown certification data variants
1305                _ => return Err("Unknown CertificationData variant encountered".to_string()),
1306            },
1307        })
1308    }
1309}
1310
1311#[derive(Debug, Deserialize)]
1312#[serde(rename_all = "camelCase")]
1313pub struct TeeAttestRequest {
1314    /// Client-provided nonce for freshness (32 bytes as hex string)
1315    pub nonce: String,
1316    /// Optional application ID to include in attestation
1317    /// If provided, the application's bytecode BlobId (hash) will be included in report_data
1318    pub application_id: Option<ApplicationId>,
1319}
1320
1321impl TeeAttestRequest {
1322    pub fn new(nonce: String, application_id: Option<ApplicationId>) -> Self {
1323        Self {
1324            nonce,
1325            application_id,
1326        }
1327    }
1328}
1329
1330#[derive(Debug, Serialize)]
1331#[serde(rename_all = "camelCase")]
1332pub struct TeeInfoResponseData {
1333    /// Cloud provider (e.g., "gcp", "azure", "unknown")
1334    pub cloud_provider: String,
1335    /// OS image name (e.g., "ubuntu-2404-tdx-v20250115")
1336    pub os_image: String,
1337    /// MRTD extracted from TD report (48 bytes hex)
1338    pub mrtd: String,
1339}
1340
1341#[derive(Debug, Serialize)]
1342#[serde(rename_all = "camelCase")]
1343pub struct TeeInfoResponse {
1344    pub data: TeeInfoResponseData,
1345}
1346
1347impl TeeInfoResponse {
1348    pub fn new(cloud_provider: String, os_image: String, mrtd: String) -> Self {
1349        Self {
1350            data: TeeInfoResponseData {
1351                cloud_provider,
1352                os_image,
1353                mrtd,
1354            },
1355        }
1356    }
1357}
1358
1359#[derive(Debug, Serialize)]
1360#[serde(rename_all = "camelCase")]
1361pub struct TeeAttestResponseData {
1362    /// Base64-encoded TDX quote
1363    /// The quote contains the report_data which the client must verify
1364    pub quote_b64: String,
1365    /// Parsed TDX quote structure
1366    pub quote: Quote,
1367}
1368
1369#[derive(Debug, Serialize)]
1370#[serde(rename_all = "camelCase")]
1371pub struct TeeAttestResponse {
1372    pub data: TeeAttestResponseData,
1373}
1374
1375impl TeeAttestResponse {
1376    pub fn new(quote_b64: String, quote: Quote) -> Self {
1377        Self {
1378            data: TeeAttestResponseData { quote_b64, quote },
1379        }
1380    }
1381}
1382
1383#[derive(Debug, Deserialize)]
1384#[serde(rename_all = "camelCase")]
1385pub struct TeeVerifyQuoteRequest {
1386    /// Base64-encoded TDX quote to verify
1387    pub quote_b64: String,
1388    /// Client-provided nonce that should match report_data[0..32] (64 hex chars = 32 bytes)
1389    pub nonce: String,
1390    /// Optional expected application hash that should match report_data[32..64] (64 hex chars = 32 bytes)
1391    pub expected_application_hash: Option<String>,
1392}
1393
1394#[derive(Debug, Serialize)]
1395#[serde(rename_all = "camelCase")]
1396pub struct TeeVerifyQuoteResponseData {
1397    /// Whether the quote signature and certificate chain are valid
1398    pub quote_verified: bool,
1399    /// Whether the nonce matches report_data[0..32]
1400    pub nonce_verified: bool,
1401    /// Whether the application hash matches report_data[32..64] (if provided)
1402    pub application_hash_verified: Option<bool>,
1403    /// Parsed quote structure
1404    pub quote: Quote,
1405}
1406
1407#[derive(Debug, Serialize)]
1408#[serde(rename_all = "camelCase")]
1409pub struct TeeVerifyQuoteResponse {
1410    pub data: TeeVerifyQuoteResponseData,
1411}
1412
1413impl TeeVerifyQuoteResponse {
1414    pub fn new(data: TeeVerifyQuoteResponseData) -> Self {
1415        Self { data }
1416    }
1417}
1418
1419// -------------------------------------------- Validation Implementations --------------------------------------------
1420//
1421// Validation Strategy:
1422// ====================
1423// These implementations focus on validating user-controlled string fields and size limits.
1424//
1425// Types like `ContextId`, `PublicKey`, `ApplicationId`, and `ProposalId` are validated during
1426// serde deserialization - they implement `FromStr` which performs format validation (e.g.,
1427// base58 decoding, length checks). If deserialization succeeds, the type is guaranteed valid.
1428//
1429// For request types containing only these strongly-typed fields, the `Validate` impl returns
1430// an empty Vec since no additional runtime validation is needed beyond what serde already does.
1431//
1432// This approach provides:
1433// 1. Type-safe validation at the deserialization boundary
1434// 2. Additional size/format checks for user-provided strings (method names, URLs, etc.)
1435// 3. Protection against oversized payloads that could cause resource exhaustion
1436
1437use crate::validation::{
1438    helpers::{
1439        validate_bytes_size, validate_hex_string, validate_limit, validate_offset,
1440        validate_optional_hex_string, validate_optional_string_length, validate_string_length,
1441        validate_url,
1442    },
1443    Validate, ValidationError, MAX_CONTEXT_KEY_LENGTH, MAX_INIT_PARAMS_SIZE, MAX_METADATA_SIZE,
1444    MAX_METHOD_NAME_LENGTH, MAX_NONCE_LENGTH, MAX_PACKAGE_NAME_LENGTH, MAX_PATH_LENGTH,
1445    MAX_PROTOCOL_LENGTH, MAX_QUOTE_B64_LENGTH, MAX_VALID_FOR_BLOCKS, MAX_VERSION_LENGTH,
1446};
1447
1448impl Validate for InstallApplicationRequest {
1449    fn validate(&self) -> Vec<ValidationError> {
1450        let mut errors = Vec::new();
1451
1452        if let Some(e) = validate_url(&self.url, "url") {
1453            errors.push(e);
1454        }
1455
1456        if let Some(e) = validate_bytes_size(&self.metadata, "metadata", MAX_METADATA_SIZE) {
1457            errors.push(e);
1458        }
1459
1460        if let Some(e) =
1461            validate_optional_string_length(&self.package, "package", MAX_PACKAGE_NAME_LENGTH)
1462        {
1463            errors.push(e);
1464        }
1465
1466        if let Some(e) =
1467            validate_optional_string_length(&self.version, "version", MAX_VERSION_LENGTH)
1468        {
1469            errors.push(e);
1470        }
1471
1472        errors
1473    }
1474}
1475
1476impl Validate for InstallDevApplicationRequest {
1477    fn validate(&self) -> Vec<ValidationError> {
1478        let mut errors = Vec::new();
1479
1480        if self.path.as_str().len() > MAX_PATH_LENGTH {
1481            errors.push(ValidationError::StringTooLong {
1482                field: "path",
1483                max: MAX_PATH_LENGTH,
1484                actual: self.path.as_str().len(),
1485            });
1486        }
1487
1488        if let Some(e) = validate_bytes_size(&self.metadata, "metadata", MAX_METADATA_SIZE) {
1489            errors.push(e);
1490        }
1491
1492        if let Some(e) =
1493            validate_optional_string_length(&self.package, "package", MAX_PACKAGE_NAME_LENGTH)
1494        {
1495            errors.push(e);
1496        }
1497
1498        if let Some(e) =
1499            validate_optional_string_length(&self.version, "version", MAX_VERSION_LENGTH)
1500        {
1501            errors.push(e);
1502        }
1503
1504        errors
1505    }
1506}
1507
1508impl Validate for CreateContextRequest {
1509    fn validate(&self) -> Vec<ValidationError> {
1510        let mut errors = Vec::new();
1511
1512        if let Some(e) = validate_string_length(&self.protocol, "protocol", MAX_PROTOCOL_LENGTH) {
1513            errors.push(e);
1514        }
1515
1516        if self.protocol.is_empty() {
1517            errors.push(ValidationError::EmptyField { field: "protocol" });
1518        }
1519
1520        if let Some(e) = validate_bytes_size(
1521            &self.initialization_params,
1522            "initialization_params",
1523            MAX_INIT_PARAMS_SIZE,
1524        ) {
1525            errors.push(e);
1526        }
1527
1528        errors
1529    }
1530}
1531
1532impl Validate for InviteToContextRequest {
1533    fn validate(&self) -> Vec<ValidationError> {
1534        // Fields: context_id (ContextId), inviter_id (PublicKey), invitee_id (PublicKey)
1535        // All fields are validated during serde deserialization via FromStr implementations.
1536        // ContextId and PublicKey validate format (base58, length) at parse time.
1537        Vec::new()
1538    }
1539}
1540
1541impl Validate for InviteToContextOpenInvitationRequest {
1542    fn validate(&self) -> Vec<ValidationError> {
1543        let mut errors = Vec::new();
1544
1545        if self.valid_for_blocks > MAX_VALID_FOR_BLOCKS {
1546            errors.push(ValidationError::ValueTooLarge {
1547                field: "valid_for_blocks",
1548                max: MAX_VALID_FOR_BLOCKS,
1549                actual: self.valid_for_blocks,
1550            });
1551        }
1552
1553        errors
1554    }
1555}
1556
1557impl Validate for InviteSpecializedNodeRequest {
1558    fn validate(&self) -> Vec<ValidationError> {
1559        // All fields are typed (ContextId, Option<PublicKey>) which have their own validation
1560        Vec::new()
1561    }
1562}
1563
1564impl Validate for JoinContextRequest {
1565    fn validate(&self) -> Vec<ValidationError> {
1566        // ContextInvitationPayload is a typed structure with its own validation
1567        Vec::new()
1568    }
1569}
1570
1571impl Validate for JoinContextByOpenInvitationRequest {
1572    fn validate(&self) -> Vec<ValidationError> {
1573        // All fields are typed (SignedOpenInvitation, PublicKey) which have their own validation
1574        Vec::new()
1575    }
1576}
1577
1578impl Validate for UpdateContextApplicationRequest {
1579    fn validate(&self) -> Vec<ValidationError> {
1580        let mut errors = Vec::new();
1581
1582        // Validate migrate_method if provided
1583        if let Some(ref method) = self.migrate_method {
1584            if let Some(e) =
1585                validate_string_length(method, "migrate_method", MAX_METHOD_NAME_LENGTH)
1586            {
1587                errors.push(e);
1588            }
1589
1590            if method.is_empty() {
1591                errors.push(ValidationError::EmptyField {
1592                    field: "migrate_method",
1593                });
1594            }
1595        }
1596
1597        errors
1598    }
1599}
1600
1601impl Validate for GrantPermissionRequest {
1602    fn validate(&self) -> Vec<ValidationError> {
1603        // Note: This is defined in grant_capabilities.rs handler, not here
1604        // But we still validate the admin.rs version if used
1605        Vec::new()
1606    }
1607}
1608
1609impl Validate for RevokePermissionRequest {
1610    fn validate(&self) -> Vec<ValidationError> {
1611        // All fields are typed which have their own validation
1612        Vec::new()
1613    }
1614}
1615
1616impl Validate for GetProposalsRequest {
1617    fn validate(&self) -> Vec<ValidationError> {
1618        let mut errors = Vec::new();
1619
1620        if let Some(e) = validate_offset(self.offset, "offset") {
1621            errors.push(e);
1622        }
1623
1624        if let Some(e) = validate_limit(self.limit, "limit") {
1625            errors.push(e);
1626        }
1627
1628        errors
1629    }
1630}
1631
1632impl Validate for GetContextValueRequest {
1633    fn validate(&self) -> Vec<ValidationError> {
1634        let mut errors = Vec::new();
1635
1636        if let Some(e) = validate_string_length(&self.key, "key", MAX_CONTEXT_KEY_LENGTH) {
1637            errors.push(e);
1638        }
1639
1640        if self.key.is_empty() {
1641            errors.push(ValidationError::EmptyField { field: "key" });
1642        }
1643
1644        errors
1645    }
1646}
1647
1648impl Validate for GetContextStorageEntriesRequest {
1649    fn validate(&self) -> Vec<ValidationError> {
1650        let mut errors = Vec::new();
1651
1652        if let Some(e) = validate_offset(self.offset, "offset") {
1653            errors.push(e);
1654        }
1655
1656        if let Some(e) = validate_limit(self.limit, "limit") {
1657            errors.push(e);
1658        }
1659
1660        errors
1661    }
1662}
1663
1664impl Validate for CreateAndApproveProposalRequest {
1665    fn validate(&self) -> Vec<ValidationError> {
1666        // All fields are typed (PublicKey, Proposal) which have their own validation
1667        Vec::new()
1668    }
1669}
1670
1671impl Validate for ApproveProposalRequest {
1672    fn validate(&self) -> Vec<ValidationError> {
1673        // All fields are typed (PublicKey, ProposalId) which have their own validation
1674        Vec::new()
1675    }
1676}
1677
1678impl Validate for TeeAttestRequest {
1679    fn validate(&self) -> Vec<ValidationError> {
1680        let mut errors = Vec::new();
1681
1682        // Nonce must be exactly 64 hex characters (32 bytes)
1683        if let Some(e) = validate_hex_string(&self.nonce, "nonce", 32) {
1684            errors.push(e);
1685        }
1686
1687        errors
1688    }
1689}
1690
1691impl Validate for TeeVerifyQuoteRequest {
1692    fn validate(&self) -> Vec<ValidationError> {
1693        let mut errors = Vec::new();
1694
1695        // Quote base64 size limit
1696        if self.quote_b64.len() > MAX_QUOTE_B64_LENGTH {
1697            errors.push(ValidationError::StringTooLong {
1698                field: "quote_b64",
1699                max: MAX_QUOTE_B64_LENGTH,
1700                actual: self.quote_b64.len(),
1701            });
1702        }
1703
1704        // Nonce must be exactly 64 hex characters (32 bytes)
1705        if let Some(e) = validate_hex_string(&self.nonce, "nonce", 32) {
1706            errors.push(e);
1707        }
1708
1709        // Expected application hash must be exactly 64 hex characters (32 bytes) if provided
1710        if let Some(e) = validate_optional_hex_string(
1711            &self.expected_application_hash,
1712            "expected_application_hash",
1713            32,
1714        ) {
1715            errors.push(e);
1716        }
1717
1718        errors
1719    }
1720}
1721
1722impl Validate for AddPublicKeyRequest {
1723    fn validate(&self) -> Vec<ValidationError> {
1724        let mut errors = Vec::new();
1725
1726        // Validate nonce in SignatureMessage
1727        if let Some(e) = validate_string_length(
1728            &self.payload.message.nonce,
1729            "payload.message.nonce",
1730            MAX_NONCE_LENGTH,
1731        ) {
1732            errors.push(e);
1733        }
1734
1735        errors
1736    }
1737}
1738
1739impl Validate for JwtTokenRequest {
1740    fn validate(&self) -> Vec<ValidationError> {
1741        let mut errors = Vec::new();
1742
1743        // executor_public_key should be a reasonable length
1744        if self.executor_public_key.len() > 128 {
1745            errors.push(ValidationError::StringTooLong {
1746                field: "executor_public_key",
1747                max: 128,
1748                actual: self.executor_public_key.len(),
1749            });
1750        }
1751
1752        if self.executor_public_key.is_empty() {
1753            errors.push(ValidationError::EmptyField {
1754                field: "executor_public_key",
1755            });
1756        }
1757
1758        errors
1759    }
1760}
1761
1762impl Validate for JwtRefreshRequest {
1763    fn validate(&self) -> Vec<ValidationError> {
1764        let mut errors = Vec::new();
1765
1766        // Refresh tokens are typically JWTs which shouldn't exceed a reasonable size
1767        if self.refresh_token.len() > 4096 {
1768            errors.push(ValidationError::StringTooLong {
1769                field: "refresh_token",
1770                max: 4096,
1771                actual: self.refresh_token.len(),
1772            });
1773        }
1774
1775        if self.refresh_token.is_empty() {
1776            errors.push(ValidationError::EmptyField {
1777                field: "refresh_token",
1778            });
1779        }
1780
1781        errors
1782    }
1783}