Skip to main content

saddle_service/
c5_compiled.rs

1//! Compile-time closed C4/C5 Service execution candidate.
2//!
3//! The application generator owns the concrete dispatcher type. This module
4//! supplies only fixed payloads, type-derived layout proof, and the linear
5//! lookup/execution assembly. It deliberately does not erase handlers or
6//! futures.
7
8use std::{
9    any::TypeId,
10    future::Future,
11    marker::PhantomData,
12    mem,
13    pin::Pin,
14    task::{Context, Poll},
15};
16
17use saddle_admission::{
18    DbRequestPermit, DbRouteCreditDemand, DbRouteResources, ManagedBytes, ManagedResponse,
19    ManagedResponseBuilder, RequestMemory,
20};
21use saddle_db::internal::{
22    ManagedWriteResult, QueryOptionalExecution, QueryOptionalInvocation,
23    QueryOptionalOperationProof, StaticQueryOptionalOperation, StaticWriteOperation,
24    TransactionExecution, TransactionInvocation, TransactionOperationProof, WriteExecution,
25    WriteInvocation, WriteOperationProof,
26};
27
28use crate::{
29    Service,
30    c4_entry::{
31        EntryCapabilityLimits, EntryFraming, EntryPlanSpec, RegisteredDbDemand, RegisteredEntry,
32        RegisteredRouteExecutionProof, RegistryError, ServiceEntryCapability,
33        ServiceEntryCapabilityBuilder,
34    },
35    registry::ServiceDescriptor,
36};
37
38const SERVICE_CAPACITY_LEAF_SCHEMA: &[u8] = b"saddle.service-capacity-leaf.v1";
39
40/// Generator/Gate-owned artifact binding consumed by the sole Service leaf
41/// assembly. It carries identities only; no resource or capacity array crosses
42/// this boundary.
43#[doc(hidden)]
44pub trait GeneratedServiceCapacityArtifactOwner: Sized {
45    fn build_identity(&self) -> [u8; 32];
46    fn artifact_identity(&self) -> [u8; 32];
47    fn owner_generation(&self) -> u64;
48}
49
50#[derive(Debug)]
51pub(super) struct ServiceCapacityRouteRecord {
52    pub(super) identity: u64,
53    pub(super) commitment: usize,
54    pub(super) managed_objects_peak: usize,
55    pub(super) db_connections: usize,
56    pub(super) db_operations: usize,
57    pub(super) source_identity: [u8; 32],
58}
59
60#[derive(Clone, Copy)]
61enum ManagedOwnerContribution {
62    RequestBody,
63    ResponseReservation,
64}
65
66impl ManagedOwnerContribution {
67    const fn count(self) -> usize {
68        match self {
69            Self::RequestBody | Self::ResponseReservation => 1,
70        }
71    }
72}
73
74fn compiled_route_managed_objects_peak() -> Result<usize, ExecutionError> {
75    [
76        ManagedOwnerContribution::RequestBody,
77        ManagedOwnerContribution::ResponseReservation,
78    ]
79    .into_iter()
80    .try_fold(0_usize, |peak, contribution| {
81        peak.checked_add(contribution.count())
82            .ok_or(ExecutionError::OutputTooLarge)
83    })
84}
85
86/// Non-replayable Service-owned leaf for Admission's multi-leaf verifier.
87/// All fields are derived during the same freeze that creates execution.
88#[doc(hidden)]
89#[derive(Debug)]
90pub(super) struct FrozenServiceCapacitySourceLeaf {
91    pub(super) leaf_identity: [u8; 32],
92    pub(super) common_identities: [[u8; 32]; 3],
93    owner_generation: u64,
94    pub(super) route_type_closure_identity: [u8; 32],
95    pub(super) routes: Box<[ServiceCapacityRouteRecord]>,
96}
97
98/// Borrowed raw capacity view. It observes the Service-owned bundle but is not
99/// an independently owned proof or an authorization capability.
100#[doc(hidden)]
101pub struct FrozenServiceCapacitySourceView<'a> {
102    leaf: &'a FrozenServiceCapacitySourceLeaf,
103}
104
105impl saddle_admission::ServiceCapacitySourceLeaf for FrozenServiceCapacitySourceView<'_> {
106    fn leaf_identity(&self) -> [u8; 32] {
107        self.leaf.leaf_identity
108    }
109
110    fn common_identities(&self) -> [[u8; 32]; 3] {
111        self.leaf.common_identities
112    }
113
114    fn owner_generation(&self) -> u64 {
115        self.leaf.owner_generation
116    }
117
118    fn route_type_closure_identity(&self) -> [u8; 32] {
119        self.leaf.route_type_closure_identity
120    }
121
122    fn route_count(&self) -> usize {
123        self.leaf.routes.len()
124    }
125
126    fn route_identity(&self, index: usize) -> Option<u64> {
127        self.leaf.routes.get(index).map(|route| route.identity)
128    }
129
130    fn managed_commitment_bytes(&self, index: usize) -> Option<usize> {
131        self.leaf.routes.get(index).map(|route| route.commitment)
132    }
133
134    fn managed_objects_peak(&self, index: usize) -> Option<usize> {
135        self.leaf
136            .routes
137            .get(index)
138            .map(|route| route.managed_objects_peak)
139    }
140
141    fn db_connections(&self, index: usize) -> Option<usize> {
142        self.leaf
143            .routes
144            .get(index)
145            .map(|route| route.db_connections)
146    }
147
148    fn db_operations(&self, index: usize) -> Option<usize> {
149        self.leaf.routes.get(index).map(|route| route.db_operations)
150    }
151}
152
153struct ServiceCapacityDigest([u64; 4]);
154
155impl ServiceCapacityDigest {
156    fn new() -> Self {
157        Self([
158            0xcbf2_9ce4_8422_2325,
159            0x9e37_79b9_7f4a_7c15,
160            0x6a09_e667_f3bc_c909,
161            0xbb67_ae85_84ca_a73b,
162        ])
163    }
164
165    fn write(&mut self, bytes: &[u8]) {
166        const PRIMES: [u64; 4] = [
167            0x0000_0100_0000_01b3,
168            0x9e37_79b1_85eb_ca87,
169            0xc2b2_ae3d_27d4_eb4f,
170            0x1656_67b1_9e37_79f9,
171        ];
172        for (index, byte) in bytes.iter().copied().enumerate() {
173            for (lane, prime) in self.0.iter_mut().zip(PRIMES) {
174                *lane ^= u64::from(byte).wrapping_add(index as u64);
175                *lane = lane.wrapping_mul(prime);
176                *lane ^= *lane >> 29;
177            }
178        }
179    }
180
181    fn usize(&mut self, value: usize) {
182        self.write(&value.to_le_bytes());
183    }
184
185    fn finish(self) -> [u8; 32] {
186        let mut output = [0; 32];
187        for (index, lane) in self.0.into_iter().enumerate() {
188            output[index * 8..(index + 1) * 8].copy_from_slice(&lane.to_le_bytes());
189        }
190        output
191    }
192}
193
194struct ServiceRouteSourceFacts<'a> {
195    route_token: &'a [u8],
196    framing: EntryFraming,
197    identity: crate::c4_entry::RegisteredEntryIdentity,
198    layout_source_identity: [u8; 32],
199    commitment: usize,
200    managed_objects_peak: usize,
201    db_connections: usize,
202    db_operations: usize,
203}
204
205fn service_route_source_identity(facts: ServiceRouteSourceFacts<'_>) -> [u8; 32] {
206    let mut digest = ServiceCapacityDigest::new();
207    digest.write(SERVICE_CAPACITY_LEAF_SCHEMA);
208    digest.write(b"route-source");
209    digest.usize(facts.route_token.len());
210    digest.write(facts.route_token);
211    digest.write(&facts.framing.source_code());
212    digest.write(&facts.identity.contract().opaque().to_le_bytes());
213    digest.write(&facts.identity.plan().opaque().to_le_bytes());
214    digest.write(&facts.identity.factory().opaque().to_le_bytes());
215    digest.write(&facts.layout_source_identity);
216    digest.usize(facts.commitment);
217    digest.usize(facts.managed_objects_peak);
218    digest.usize(facts.db_connections);
219    digest.usize(facts.db_operations);
220    digest.finish()
221}
222
223fn service_route_type_closure_identity(
224    artifact: [u8; 32],
225    routes: &[ServiceCapacityRouteRecord],
226) -> [u8; 32] {
227    let mut digest = ServiceCapacityDigest::new();
228    digest.write(SERVICE_CAPACITY_LEAF_SCHEMA);
229    digest.write(b"route-type-owner-closure");
230    digest.write(&artifact);
231    digest.write(b"managed-request-body-owner");
232    digest.write(b"managed-response-reservation-owner");
233    digest.usize(routes.len());
234    for route in routes {
235        digest.write(&route.source_identity);
236        digest.usize(route.managed_objects_peak);
237    }
238    digest.finish()
239}
240
241fn service_route_set_identity(routes: &[ServiceCapacityRouteRecord]) -> [u8; 32] {
242    let mut digest = ServiceCapacityDigest::new();
243    digest.write(SERVICE_CAPACITY_LEAF_SCHEMA);
244    digest.write(b"route-set");
245    digest.usize(routes.len());
246    for route in routes {
247        digest.write(&route.source_identity);
248    }
249    digest.finish()
250}
251
252fn service_leaf_identity(
253    build: [u8; 32],
254    artifact: [u8; 32],
255    route_set: [u8; 32],
256    owner_generation: u64,
257    route_type_closure: [u8; 32],
258    routes: &[ServiceCapacityRouteRecord],
259) -> [u8; 32] {
260    let mut digest = ServiceCapacityDigest::new();
261    digest.write(SERVICE_CAPACITY_LEAF_SCHEMA);
262    digest.write(b"leaf");
263    digest.write(&build);
264    digest.write(&artifact);
265    digest.write(&route_set);
266    digest.write(&owner_generation.to_le_bytes());
267    digest.write(&route_type_closure);
268    digest.usize(routes.len());
269    for route in routes {
270        digest.write(&route.source_identity);
271    }
272    digest.finish()
273}
274
275/// The only assembly that emits a production Service capacity leaf.
276#[doc(hidden)]
277pub struct CompiledExecutionWithCapacityLeaf<E, C, F, const BODY: usize, const OUTPUT: usize>
278where
279    E: Fn(usize, C, ManagedBytes, CompiledDbPermit, ManagedResponseBuilder) -> F
280        + Send
281        + Sync
282        + 'static,
283    C: Send + Unpin + 'static,
284    F: Future<Output = Result<ManagedResponse, ExecutionError>> + Send + 'static,
285{
286    pub(super) execution: CompiledExecutionCapability<E, C, F, BODY, OUTPUT>,
287    pub(super) service_leaf: FrozenServiceCapacitySourceLeaf,
288}
289
290impl<E, C, F, const BODY: usize, const OUTPUT: usize>
291    CompiledExecutionWithCapacityLeaf<E, C, F, BODY, OUTPUT>
292where
293    E: Fn(usize, C, ManagedBytes, CompiledDbPermit, ManagedResponseBuilder) -> F
294        + Send
295        + Sync
296        + 'static,
297    C: Send + Unpin + 'static,
298    F: Future<Output = Result<ManagedResponse, ExecutionError>> + Send + 'static,
299{
300    /// Borrows the raw capacity facts without separating either owned half.
301    pub fn capacity_source(&self) -> FrozenServiceCapacitySourceView<'_> {
302        FrozenServiceCapacitySourceView {
303            leaf: &self.service_leaf,
304        }
305    }
306
307    /// Produces deterministic Service-owned machine facts from this frozen
308    /// whole. The result is an observation only and carries no authority.
309    #[doc(hidden)]
310    pub fn production_fact_input(
311        &self,
312    ) -> Result<
313        crate::production_fact::ServiceProductionFactInput,
314        crate::production_fact::ServiceProductionFactError,
315    > {
316        crate::production_fact::service_production_fact_input(self)
317    }
318
319    /// Resolves through the registry frozen in this same bundle.
320    pub fn lookup(
321        &self,
322        route_token: &[u8],
323        framing: EntryFraming,
324        declared_length: Option<u64>,
325    ) -> Result<RegisteredRouteExecutionProof, RegistryError> {
326        self.execution.lookup(route_token, framing, declared_length)
327    }
328}
329
330#[derive(Clone, Copy, Debug, Eq, PartialEq)]
331pub enum ExecutionError {
332    Registry(RegistryError),
333    CompiledRegistry(CompiledRegistryError),
334    UnknownExecution,
335    InputTooLarge,
336    InvalidInput,
337    OutputTooLarge,
338    RouteResourcesMismatch,
339    ResponseReservationFailed,
340    Business,
341    DependencyUnavailable,
342    /// A dependency accepted this request but failed while executing,
343    /// decoding its result or finalizing the operation.
344    DependencyExecutionFailed,
345    MissingCapacityIdentity,
346}
347
348impl From<RegistryError> for ExecutionError {
349    fn from(error: RegistryError) -> Self {
350        Self::Registry(error)
351    }
352}
353
354impl From<CompiledRegistryError> for ExecutionError {
355    fn from(error: CompiledRegistryError) -> Self {
356        Self::CompiledRegistry(error)
357    }
358}
359
360impl ExecutionError {
361    /// Exhaustive protocol classification for every generated execution
362    /// failure. Adding an execution error cannot silently acquire a response
363    /// meaning because this match has no catch-all arm.
364    pub const fn response_outcome_class(
365        self,
366    ) -> saddle_runtime::compiled_route::ResponseOutcomeClass {
367        use saddle_runtime::compiled_route::ResponseOutcomeClass;
368
369        match self {
370            Self::InputTooLarge | Self::InvalidInput => ResponseOutcomeClass::InvalidRequest,
371            Self::Business => ResponseOutcomeClass::BusinessRejected,
372            Self::RouteResourcesMismatch | Self::DependencyUnavailable => {
373                ResponseOutcomeClass::Unavailable
374            }
375            Self::Registry(_)
376            | Self::CompiledRegistry(_)
377            | Self::UnknownExecution
378            | Self::OutputTooLarge
379            | Self::ResponseReservationFailed
380            | Self::DependencyExecutionFailed => ResponseOutcomeClass::Internal,
381            Self::MissingCapacityIdentity => ResponseOutcomeClass::Internal,
382        }
383    }
384}
385
386#[derive(Clone, Copy, Debug, Eq, PartialEq)]
387pub enum CompiledRegistryError {
388    InvalidCapacity,
389    AllocationFailed,
390    CapacityExceeded,
391    EmptyRegistry,
392    DuplicateContract,
393    DuplicateIdentity,
394}
395
396struct CompiledServiceEntry {
397    contract: TypeId,
398    _descriptor: ServiceDescriptor,
399}
400
401/// Startup-only marker registry for the closed 0.2 execution graph.
402///
403/// It contains no implementation, handler, factory, observer, client or
404/// dispatcher capability and deliberately does not implement Clone.
405pub struct CompiledServiceRegistryBuilder {
406    entries: Vec<CompiledServiceEntry>,
407    max_services: usize,
408}
409
410impl CompiledServiceRegistryBuilder {
411    pub fn new(max_services: usize) -> Result<Self, CompiledRegistryError> {
412        if max_services == 0 || max_services > u32::MAX as usize {
413            return Err(CompiledRegistryError::InvalidCapacity);
414        }
415        let mut entries = Vec::new();
416        entries
417            .try_reserve_exact(max_services)
418            .map_err(|_| CompiledRegistryError::AllocationFailed)?;
419        Ok(Self {
420            entries,
421            max_services,
422        })
423    }
424
425    pub fn register<S>(
426        &mut self,
427        descriptor: ServiceDescriptor,
428    ) -> Result<(), CompiledRegistryError>
429    where
430        S: Service,
431    {
432        if self.entries.len() == self.max_services {
433            return Err(CompiledRegistryError::CapacityExceeded);
434        }
435        let contract = TypeId::of::<S>();
436        if self.entries.iter().any(|entry| entry.contract == contract) {
437            return Err(CompiledRegistryError::DuplicateContract);
438        }
439        if self
440            .entries
441            .iter()
442            .any(|entry| entry._descriptor == descriptor)
443        {
444            return Err(CompiledRegistryError::DuplicateIdentity);
445        }
446        self.entries.push(CompiledServiceEntry {
447            contract,
448            _descriptor: descriptor,
449        });
450        Ok(())
451    }
452
453    pub fn freeze(self) -> Result<CompiledServiceRegistry, CompiledRegistryError> {
454        if self.entries.is_empty() {
455            return Err(CompiledRegistryError::EmptyRegistry);
456        }
457        Ok(CompiledServiceRegistry {
458            entries: self.entries,
459        })
460    }
461}
462
463/// Linear compiled-only identity owner. No public API exposes its ordinal.
464pub struct CompiledServiceRegistry {
465    entries: Vec<CompiledServiceEntry>,
466}
467
468impl CompiledServiceRegistry {
469    pub(super) fn internal_entry_count(&self) -> usize {
470        self.entries.len()
471    }
472
473    pub(super) fn internal_entry_index<S>(&self) -> Option<usize>
474    where
475        S: Service,
476    {
477        self.entries
478            .iter()
479            .position(|entry| entry.contract == TypeId::of::<S>())
480    }
481}
482
483mod sealed {
484    pub trait ManagedValue {}
485    pub trait ManagedCodec {}
486}
487
488/// Only Saddle-owned fixed-layout values can enter the generated 0.2 graph.
489pub trait ManagedValue: sealed::ManagedValue + Send + 'static {}
490
491#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
492pub struct ManagedU64(pub u64);
493
494impl sealed::ManagedValue for ManagedU64 {}
495impl ManagedValue for ManagedU64 {}
496
497#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
498pub struct ManagedBool(pub bool);
499
500impl sealed::ManagedValue for ManagedBool {}
501impl ManagedValue for ManagedBool {}
502
503#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
504pub struct ManagedPair<A: ManagedValue, B: ManagedValue>(pub A, pub B);
505
506impl<A: ManagedValue, B: ManagedValue> sealed::ManagedValue for ManagedPair<A, B> {}
507impl<A: ManagedValue, B: ManagedValue> ManagedValue for ManagedPair<A, B> {}
508
509/// A codec contribution is sealed and tied to one concrete request/response
510/// pair. The generator chooses it; business code cannot implement it.
511pub trait ManagedCodec<Request: ManagedValue, Response: ManagedValue>:
512    sealed::ManagedCodec + Send + 'static
513{
514    const WORKSPACE_BYTES: usize;
515}
516
517pub struct FixedManagedCodec<Request: ManagedValue, Response: ManagedValue>(
518    PhantomData<(Request, Response)>,
519);
520
521impl<Request: ManagedValue, Response: ManagedValue> sealed::ManagedCodec
522    for FixedManagedCodec<Request, Response>
523{
524}
525
526impl<Request: ManagedValue, Response: ManagedValue> ManagedCodec<Request, Response>
527    for FixedManagedCodec<Request, Response>
528{
529    const WORKSPACE_BYTES: usize = mem::size_of::<Request>() + mem::size_of::<Response>();
530}
531
532/// Saddle-owned, inline byte storage. It never allocates or grows.
533#[derive(Debug)]
534pub struct FixedBytes<const N: usize> {
535    bytes: [u8; N],
536    length: usize,
537}
538
539impl<const N: usize> FixedBytes<N> {
540    pub const fn empty() -> Self {
541        Self {
542            bytes: [0; N],
543            length: 0,
544        }
545    }
546
547    pub fn try_from_slice(value: &[u8]) -> Result<Self, ExecutionError> {
548        if value.len() > N {
549            return Err(ExecutionError::InputTooLarge);
550        }
551        let mut output = Self::empty();
552        output.bytes[..value.len()].copy_from_slice(value);
553        output.length = value.len();
554        Ok(output)
555    }
556
557    pub const fn capacity(&self) -> usize {
558        N
559    }
560
561    pub const fn len(&self) -> usize {
562        self.length
563    }
564
565    pub const fn is_empty(&self) -> bool {
566        self.length == 0
567    }
568
569    pub fn as_slice(&self) -> &[u8] {
570        &self.bytes[..self.length]
571    }
572
573    pub fn try_append(&mut self, value: &[u8]) -> Result<(), ExecutionError> {
574        let end = self
575            .length
576            .checked_add(value.len())
577            .ok_or(ExecutionError::OutputTooLarge)?;
578        if end > N {
579            return Err(ExecutionError::OutputTooLarge);
580        }
581        self.bytes[self.length..end].copy_from_slice(value);
582        self.length = end;
583        Ok(())
584    }
585}
586
587/// Layout proof can only be created from the concrete types named by generated
588/// dispatcher code. No numeric constructor is exposed.
589#[derive(Clone, Copy, Debug, Eq, PartialEq)]
590pub struct ExecutionLayoutProof {
591    service: TypeId,
592    binding: TypeId,
593    body_bytes: usize,
594    decode_workspace_bytes: usize,
595    request_bytes: usize,
596    future_bytes: usize,
597    response_bytes: usize,
598    encode_workspace_bytes: usize,
599    output_bytes: usize,
600}
601
602impl ExecutionLayoutProof {
603    pub fn bind<
604        S,
605        const BODY: usize,
606        const OUTPUT: usize,
607        Request,
608        Response,
609        Codec,
610        HandlerFuture,
611    >() -> Result<Self, ExecutionError>
612    where
613        S: Service<Request = Request, Response = Response>,
614        Request: ManagedValue,
615        Response: ManagedValue,
616        Codec: ManagedCodec<Request, Response>,
617        HandlerFuture: Future<Output = Result<Response, ExecutionError>> + Send + 'static,
618    {
619        let proof = Self {
620            service: TypeId::of::<S>(),
621            binding: TypeId::of::<(S, Request, Response, Codec, HandlerFuture)>(),
622            body_bytes: BODY,
623            decode_workspace_bytes: Codec::WORKSPACE_BYTES,
624            request_bytes: mem::size_of::<Request>(),
625            future_bytes: mem::size_of::<HandlerFuture>(),
626            response_bytes: mem::size_of::<Response>(),
627            encode_workspace_bytes: Codec::WORKSPACE_BYTES,
628            output_bytes: OUTPUT,
629        };
630        proof.commitment()?;
631        Ok(proof)
632    }
633
634    pub const fn body_bytes(self) -> usize {
635        self.body_bytes
636    }
637
638    pub const fn future_bytes(self) -> usize {
639        self.future_bytes
640    }
641
642    pub fn same_concrete_binding(self, other: Self) -> bool {
643        self.binding == other.binding
644    }
645
646    pub fn commitment(self) -> Result<usize, ExecutionError> {
647        [
648            self.body_bytes,
649            self.decode_workspace_bytes,
650            self.request_bytes,
651            self.future_bytes,
652            self.response_bytes,
653            self.encode_workspace_bytes,
654            self.output_bytes,
655        ]
656        .into_iter()
657        .try_fold(0_usize, |total, bytes| {
658            total
659                .checked_add(bytes)
660                .ok_or(ExecutionError::OutputTooLarge)
661        })
662    }
663
664    fn plan(self) -> Result<EntryPlanSpec, ExecutionError> {
665        let body = u64::try_from(self.body_bytes).map_err(|_| ExecutionError::OutputTooLarge)?;
666        let decode = self
667            .decode_workspace_bytes
668            .checked_add(self.request_bytes)
669            .ok_or(ExecutionError::OutputTooLarge)?;
670        let business = self.future_bytes;
671        let response = self.response_bytes;
672        let encode = self
673            .encode_workspace_bytes
674            .checked_add(self.output_bytes)
675            .ok_or(ExecutionError::OutputTooLarge)?;
676        Ok(EntryPlanSpec::new()
677            .body_hard_limit(body)
678            .managed_body_peak(body)
679            .decode_peak(u64::try_from(decode).map_err(|_| ExecutionError::OutputTooLarge)?)
680            .business_working_set_peak(
681                u64::try_from(business).map_err(|_| ExecutionError::OutputTooLarge)?,
682            )
683            .internal_call_peak(0)
684            .response_object_peak(
685                u64::try_from(response).map_err(|_| ExecutionError::OutputTooLarge)?,
686            )
687            .encode_destination_peak(
688                u64::try_from(encode).map_err(|_| ExecutionError::OutputTooLarge)?,
689            )
690            .copy_on_success_peak(0))
691    }
692
693    /// Explicitly closes a compiled route that has no DB edge.
694    pub const fn without_db(self) -> CompiledRouteExecutionProof {
695        CompiledRouteExecutionProof {
696            layout: self,
697            db_connections: 0,
698            db_operations: 0,
699            db_binding: None,
700            db_binding_conflict: false,
701        }
702    }
703
704    /// Derives route demand from a concrete, validated DB operation proof.
705    pub fn with_query_optional<O>(
706        self,
707        operation: QueryOptionalOperationProof<O>,
708    ) -> CompiledRouteExecutionProof
709    where
710        O: StaticQueryOptionalOperation,
711    {
712        let credits = operation.layout().credits();
713        let binding = CompiledDbBinding {
714            kind: CompiledDbOperationKind::QueryOptional,
715            operation: TypeId::of::<O>(),
716            parameter_bytes: operation.layout().parameter_bytes(),
717            result_bytes: operation.layout().optional_row_bytes(),
718        };
719        CompiledRouteExecutionProof {
720            layout: self,
721            db_connections: u64::from(credits.connections()),
722            db_operations: u64::from(credits.operations()),
723            db_binding: Some(binding),
724            db_binding_conflict: false,
725        }
726    }
727
728    /// Derives a standalone-write route from one concrete generated proof.
729    pub fn with_write<O>(self, operation: WriteOperationProof<O>) -> CompiledRouteExecutionProof
730    where
731        O: StaticWriteOperation,
732    {
733        let layout = operation.layout();
734        let credits = layout.credits();
735        CompiledRouteExecutionProof {
736            layout: self,
737            db_connections: u64::from(credits.connections()),
738            db_operations: u64::from(credits.operations()),
739            db_binding: Some(CompiledDbBinding {
740                kind: CompiledDbOperationKind::Write,
741                operation: TypeId::of::<O>(),
742                parameter_bytes: layout.parameter_bytes(),
743                result_bytes: mem::size_of::<ManagedWriteResult>(),
744            }),
745            db_binding_conflict: false,
746        }
747    }
748
749    /// Derives one single-level transaction containing one generated write.
750    pub fn with_transaction<O>(
751        self,
752        operation: TransactionOperationProof<O>,
753    ) -> CompiledRouteExecutionProof
754    where
755        O: StaticWriteOperation,
756    {
757        let layout = operation.layout();
758        let credits = layout.credits();
759        CompiledRouteExecutionProof {
760            layout: self,
761            db_connections: u64::from(credits.connections()),
762            db_operations: u64::from(credits.operations()),
763            db_binding: Some(CompiledDbBinding {
764                kind: CompiledDbOperationKind::Transaction,
765                operation: TypeId::of::<O>(),
766                parameter_bytes: layout.parameter_bytes(),
767                result_bytes: mem::size_of::<ManagedWriteResult>(),
768            }),
769            db_binding_conflict: false,
770        }
771    }
772}
773
774/// Generator-owned closure of execution layout and all DB route edges.
775///
776/// It has no numeric constructor. Additional operations merge by route
777/// maximum from their validated DB proofs.
778#[derive(Clone, Copy, Debug, Eq, PartialEq)]
779pub struct CompiledRouteExecutionProof {
780    layout: ExecutionLayoutProof,
781    db_connections: u64,
782    db_operations: u64,
783    db_binding: Option<CompiledDbBinding>,
784    db_binding_conflict: bool,
785}
786
787impl CompiledRouteExecutionProof {
788    fn capacity_source_identity(self) -> [u8; 32] {
789        let mut digest = ServiceCapacityDigest::new();
790        digest.write(SERVICE_CAPACITY_LEAF_SCHEMA);
791        digest.write(b"compiled-route-layout");
792        for value in [
793            self.layout.body_bytes,
794            self.layout.decode_workspace_bytes,
795            self.layout.request_bytes,
796            self.layout.future_bytes,
797            self.layout.response_bytes,
798            self.layout.encode_workspace_bytes,
799            self.layout.output_bytes,
800        ] {
801            digest.usize(value);
802        }
803        digest.write(&self.db_connections.to_le_bytes());
804        digest.write(&self.db_operations.to_le_bytes());
805        match self.db_binding {
806            None => digest.write(b"no-db"),
807            Some(binding) => {
808                digest.write(&[binding.kind as u8]);
809                digest.usize(binding.parameter_bytes);
810                digest.usize(binding.result_bytes);
811            }
812        }
813        digest.finish()
814    }
815}
816
817#[derive(Clone, Copy, Debug, Eq, PartialEq)]
818#[repr(u8)]
819enum CompiledDbOperationKind {
820    QueryOptional,
821    Write,
822    Transaction,
823}
824
825#[derive(Clone, Copy, Debug, Eq, PartialEq)]
826struct CompiledDbBinding {
827    kind: CompiledDbOperationKind,
828    operation: TypeId,
829    parameter_bytes: usize,
830    result_bytes: usize,
831}
832
833/// Linear DB resource passed only after matching the admitted permit against
834/// the frozen route proof.
835pub struct CompiledDbPermit {
836    inner: Option<DbRequestPermit>,
837    route_demand: RegisteredDbDemand,
838    binding: Option<CompiledDbBinding>,
839}
840
841impl CompiledDbPermit {
842    pub const fn is_none(&self) -> bool {
843        self.inner.is_none()
844    }
845
846    pub fn demand(&self) -> Option<(usize, usize)> {
847        self.inner.as_ref().map(|permit| {
848            let demand = permit.demand();
849            (demand.connections(), demand.operations())
850        })
851    }
852
853    /// Consumes the one admitted route permit directly into Database's opaque
854    /// typed execution constructor.
855    ///
856    /// The invocation indivisibly carries the layout produced by its sealed
857    /// query proof, while `route_demand` came from the registry proof already
858    /// authenticated by the same frozen execution capability. No raw permit
859    /// exists at the generated boundary.
860    pub fn handoff_query_optional<O>(
861        self,
862        invocation: QueryOptionalInvocation<O>,
863    ) -> Result<QueryOptionalExecution<O>, ExecutionError>
864    where
865        O: StaticQueryOptionalOperation,
866    {
867        let layout = invocation.layout();
868        let permit = self.into_permit(
869            CompiledDbBinding {
870                kind: CompiledDbOperationKind::QueryOptional,
871                operation: TypeId::of::<O>(),
872                parameter_bytes: layout.parameter_bytes(),
873                result_bytes: layout.optional_row_bytes(),
874            },
875            layout.credits().connections(),
876            layout.credits().operations(),
877        )?;
878        Ok(QueryOptionalExecution::from_compiled_handoff(
879            permit, invocation,
880        ))
881    }
882
883    /// Consumes the admitted route permit into one standalone Database write.
884    pub fn handoff_write<O>(
885        self,
886        invocation: WriteInvocation<O>,
887    ) -> Result<WriteExecution<O>, ExecutionError>
888    where
889        O: StaticWriteOperation,
890    {
891        let layout = invocation.layout();
892        let permit = self.into_permit(
893            CompiledDbBinding {
894                kind: CompiledDbOperationKind::Write,
895                operation: TypeId::of::<O>(),
896                parameter_bytes: layout.parameter_bytes(),
897                result_bytes: mem::size_of::<ManagedWriteResult>(),
898            },
899            layout.credits().connections(),
900            layout.credits().operations(),
901        )?;
902        Ok(WriteExecution::from_compiled_handoff(permit, invocation))
903    }
904
905    /// Consumes the admitted route permit into one single-level transaction.
906    pub fn handoff_transaction<O>(
907        self,
908        invocation: TransactionInvocation<O>,
909    ) -> Result<TransactionExecution<O>, ExecutionError>
910    where
911        O: StaticWriteOperation,
912    {
913        let layout = invocation.layout();
914        let permit = self.into_permit(
915            CompiledDbBinding {
916                kind: CompiledDbOperationKind::Transaction,
917                operation: TypeId::of::<O>(),
918                parameter_bytes: layout.parameter_bytes(),
919                result_bytes: mem::size_of::<ManagedWriteResult>(),
920            },
921            layout.credits().connections(),
922            layout.credits().operations(),
923        )?;
924        Ok(TransactionExecution::from_compiled_handoff(
925            permit, invocation,
926        ))
927    }
928
929    fn into_permit(
930        self,
931        binding: CompiledDbBinding,
932        connections: u32,
933        operations: u32,
934    ) -> Result<DbRequestPermit, ExecutionError> {
935        let permit = self.inner.ok_or(ExecutionError::RouteResourcesMismatch)?;
936        if self.binding != Some(binding)
937            || self.route_demand.connections() != u64::from(connections)
938            || self.route_demand.operations() != u64::from(operations)
939        {
940            permit
941                .begin_finalizing()
942                .and_then(|claim| claim.complete_after_connection_return())
943                .map_err(|_| ExecutionError::DependencyUnavailable)?;
944            return Err(ExecutionError::RouteResourcesMismatch);
945        }
946        Ok(permit)
947    }
948
949    #[cfg(test)]
950    fn complete_after_test_connection_return(mut self) {
951        self.inner
952            .take()
953            .expect("DB route must carry its admitted permit")
954            .begin_finalizing()
955            .expect("test connection return must enter finalizing")
956            .complete_after_connection_return()
957            .expect("test connection return must complete its role");
958    }
959}
960
961impl CompiledRouteExecutionProof {
962    pub fn with_query_optional<O>(mut self, operation: QueryOptionalOperationProof<O>) -> Self
963    where
964        O: StaticQueryOptionalOperation,
965    {
966        let layout = operation.layout();
967        let credits = layout.credits();
968        self.merge_db_binding(CompiledDbBinding {
969            kind: CompiledDbOperationKind::QueryOptional,
970            operation: TypeId::of::<O>(),
971            parameter_bytes: layout.parameter_bytes(),
972            result_bytes: layout.optional_row_bytes(),
973        });
974        self.db_connections = self.db_connections.max(u64::from(credits.connections()));
975        self.db_operations = self.db_operations.max(u64::from(credits.operations()));
976        self
977    }
978
979    pub fn with_write<O>(mut self, operation: WriteOperationProof<O>) -> Self
980    where
981        O: StaticWriteOperation,
982    {
983        let layout = operation.layout();
984        let credits = layout.credits();
985        self.merge_db_binding(CompiledDbBinding {
986            kind: CompiledDbOperationKind::Write,
987            operation: TypeId::of::<O>(),
988            parameter_bytes: layout.parameter_bytes(),
989            result_bytes: mem::size_of::<ManagedWriteResult>(),
990        });
991        self.db_connections = self.db_connections.max(u64::from(credits.connections()));
992        self.db_operations = self.db_operations.max(u64::from(credits.operations()));
993        self
994    }
995
996    pub fn with_transaction<O>(mut self, operation: TransactionOperationProof<O>) -> Self
997    where
998        O: StaticWriteOperation,
999    {
1000        let layout = operation.layout();
1001        let credits = layout.credits();
1002        self.merge_db_binding(CompiledDbBinding {
1003            kind: CompiledDbOperationKind::Transaction,
1004            operation: TypeId::of::<O>(),
1005            parameter_bytes: layout.parameter_bytes(),
1006            result_bytes: mem::size_of::<ManagedWriteResult>(),
1007        });
1008        self.db_connections = self.db_connections.max(u64::from(credits.connections()));
1009        self.db_operations = self.db_operations.max(u64::from(credits.operations()));
1010        self
1011    }
1012
1013    fn merge_db_binding(&mut self, binding: CompiledDbBinding) {
1014        if self
1015            .db_binding
1016            .is_some_and(|registered| registered != binding)
1017        {
1018            self.db_binding_conflict = true;
1019        } else {
1020            self.db_binding = Some(binding);
1021        }
1022    }
1023}
1024
1025/// Generated only for a declared static `Caller -> Callee` edge.
1026pub struct InternalCallCapability<Caller: Service, Callee: Service> {
1027    _types: PhantomData<fn(Caller) -> Callee>,
1028}
1029
1030/// Linear startup assembly. The same consumed registry produces lookup records
1031/// and selects generated execution slots.
1032pub struct CompiledExecutionBuilder<E, L, C, F, const BODY: usize, const OUTPUT: usize>
1033where
1034    E: Fn(usize, C, ManagedBytes, CompiledDbPermit, ManagedResponseBuilder) -> F
1035        + Send
1036        + Sync
1037        + 'static,
1038    L: Fn(usize) -> Option<CompiledRouteExecutionProof> + Send + Sync + 'static,
1039    C: Send + Unpin + 'static,
1040    F: Future<Output = Result<ManagedResponse, ExecutionError>> + Send + 'static,
1041{
1042    entries: ServiceEntryCapabilityBuilder,
1043    dispatcher: E,
1044    layouts: L,
1045    db_bindings: Vec<Option<CompiledDbBinding>>,
1046    capacity_routes: Vec<ServiceCapacityRouteRecord>,
1047    _types: PhantomData<fn(C) -> F>,
1048}
1049
1050impl<E, L, C, F, const BODY: usize, const OUTPUT: usize>
1051    CompiledExecutionBuilder<E, L, C, F, BODY, OUTPUT>
1052where
1053    E: Fn(usize, C, ManagedBytes, CompiledDbPermit, ManagedResponseBuilder) -> F
1054        + Send
1055        + Sync
1056        + 'static,
1057    L: Fn(usize) -> Option<CompiledRouteExecutionProof> + Send + Sync + 'static,
1058    C: Send + Unpin + 'static,
1059    F: Future<Output = Result<ManagedResponse, ExecutionError>> + Send + 'static,
1060{
1061    pub fn new(
1062        registry: CompiledServiceRegistry,
1063        dispatcher: E,
1064        layouts: L,
1065        limits: EntryCapabilityLimits,
1066    ) -> Result<Self, ExecutionError> {
1067        let entry_count = registry.internal_entry_count();
1068        let mut db_bindings = Vec::new();
1069        db_bindings
1070            .try_reserve_exact(entry_count)
1071            .map_err(|_| CompiledRegistryError::AllocationFailed)?;
1072        db_bindings.resize(entry_count, None);
1073        let mut capacity_routes = Vec::new();
1074        capacity_routes
1075            .try_reserve_exact(limits.max_routes())
1076            .map_err(|_| CompiledRegistryError::AllocationFailed)?;
1077        Ok(Self {
1078            entries: ServiceEntryCapabilityBuilder::new_compiled(registry, limits)?,
1079            dispatcher,
1080            layouts,
1081            db_bindings,
1082            capacity_routes,
1083            _types: PhantomData,
1084        })
1085    }
1086
1087    /// The plan is derived from the generated dispatcher's concrete types.
1088    /// Callers cannot supply resource numbers.
1089    pub fn expose<S>(
1090        &mut self,
1091        route_token: &[u8],
1092        framing: EntryFraming,
1093    ) -> Result<(), ExecutionError>
1094    where
1095        S: Service,
1096    {
1097        let slot = self.entries.service_index::<S>()?;
1098        let proof = (self.layouts)(slot).ok_or(ExecutionError::UnknownExecution)?;
1099        if proof.layout.service != TypeId::of::<S>()
1100            || proof.layout.body_bytes != BODY
1101            || proof.layout.output_bytes != OUTPUT
1102            || proof.db_binding_conflict
1103            || (proof.db_binding.is_none()
1104                != (proof.db_connections == 0 && proof.db_operations == 0))
1105        {
1106            return Err(ExecutionError::UnknownExecution);
1107        }
1108        if self.db_bindings[slot].is_some() && self.db_bindings[slot] != proof.db_binding {
1109            return Err(ExecutionError::UnknownExecution);
1110        }
1111        self.db_bindings[slot] = proof.db_binding;
1112        let plan = self.entries.register_compiled_plan(
1113            proof.layout.plan()?,
1114            proof.db_connections,
1115            proof.db_operations,
1116            u64::try_from(proof.layout.output_bytes).map_err(|_| ExecutionError::OutputTooLarge)?,
1117        )?;
1118        let identity = self
1119            .entries
1120            .expose_compiled::<S>(route_token, framing, plan)?;
1121        let commitment = proof.layout.commitment()?;
1122        let managed_objects_peak = compiled_route_managed_objects_peak()?;
1123        let db_connections =
1124            usize::try_from(proof.db_connections).map_err(|_| ExecutionError::OutputTooLarge)?;
1125        let db_operations =
1126            usize::try_from(proof.db_operations).map_err(|_| ExecutionError::OutputTooLarge)?;
1127        self.capacity_routes.push(ServiceCapacityRouteRecord {
1128            identity: identity.contract().opaque(),
1129            commitment,
1130            managed_objects_peak,
1131            db_connections,
1132            db_operations,
1133            source_identity: service_route_source_identity(ServiceRouteSourceFacts {
1134                route_token,
1135                framing,
1136                identity,
1137                layout_source_identity: proof.capacity_source_identity(),
1138                commitment,
1139                managed_objects_peak,
1140                db_connections,
1141                db_operations,
1142            }),
1143        });
1144        Ok(())
1145    }
1146
1147    pub fn bind_internal_call<Caller, Callee>(
1148        &self,
1149    ) -> Result<InternalCallCapability<Caller, Callee>, ExecutionError>
1150    where
1151        Caller: Service,
1152        Callee: Service,
1153    {
1154        self.entries.service_index::<Caller>()?;
1155        self.entries.service_index::<Callee>()?;
1156        Ok(InternalCallCapability {
1157            _types: PhantomData,
1158        })
1159    }
1160
1161    pub fn freeze(
1162        self,
1163    ) -> Result<CompiledExecutionCapability<E, C, F, BODY, OUTPUT>, ExecutionError> {
1164        Ok(CompiledExecutionCapability {
1165            entries: self.entries.freeze()?,
1166            dispatcher: self.dispatcher,
1167            db_bindings: self.db_bindings,
1168            _types: PhantomData,
1169        })
1170    }
1171
1172    /// Freezes execution and the sole Service source leaf from one linear
1173    /// registry assembly. Artifact identities are observations from the
1174    /// generated/Gate owner; all route facts remain Service-derived.
1175    pub fn freeze_with_capacity_leaf<A>(
1176        self,
1177        artifact: A,
1178    ) -> Result<CompiledExecutionWithCapacityLeaf<E, C, F, BODY, OUTPUT>, ExecutionError>
1179    where
1180        A: GeneratedServiceCapacityArtifactOwner,
1181    {
1182        let build = artifact.build_identity();
1183        let artifact_identity = artifact.artifact_identity();
1184        let owner_generation = artifact.owner_generation();
1185        if build == [0; 32] || artifact_identity == [0; 32] || owner_generation == 0 {
1186            return Err(ExecutionError::MissingCapacityIdentity);
1187        }
1188        if self.capacity_routes.is_empty() {
1189            return Err(ExecutionError::UnknownExecution);
1190        }
1191        let route_set_identity = service_route_set_identity(&self.capacity_routes);
1192        let route_type_closure_identity =
1193            service_route_type_closure_identity(artifact_identity, &self.capacity_routes);
1194        let leaf_identity = service_leaf_identity(
1195            build,
1196            artifact_identity,
1197            route_set_identity,
1198            owner_generation,
1199            route_type_closure_identity,
1200            &self.capacity_routes,
1201        );
1202        let service_leaf = FrozenServiceCapacitySourceLeaf {
1203            leaf_identity,
1204            common_identities: [build, artifact_identity, route_set_identity],
1205            owner_generation,
1206            route_type_closure_identity,
1207            routes: self.capacity_routes.into_boxed_slice(),
1208        };
1209        let execution = CompiledExecutionCapability {
1210            entries: self.entries.freeze()?,
1211            dispatcher: self.dispatcher,
1212            db_bindings: self.db_bindings,
1213            _types: PhantomData,
1214        };
1215        Ok(CompiledExecutionWithCapacityLeaf {
1216            execution,
1217            service_leaf,
1218        })
1219    }
1220}
1221
1222/// Non-Clone owner of both immutable lookup and the sole generated dispatcher.
1223pub struct CompiledExecutionCapability<E, C, F, const BODY: usize, const OUTPUT: usize>
1224where
1225    E: Fn(usize, C, ManagedBytes, CompiledDbPermit, ManagedResponseBuilder) -> F
1226        + Send
1227        + Sync
1228        + 'static,
1229    C: Send + Unpin + 'static,
1230    F: Future<Output = Result<ManagedResponse, ExecutionError>> + Send + 'static,
1231{
1232    pub(super) entries: ServiceEntryCapability,
1233    dispatcher: E,
1234    db_bindings: Vec<Option<CompiledDbBinding>>,
1235    _types: PhantomData<fn(C) -> F>,
1236}
1237
1238impl<E, C, F, const BODY: usize, const OUTPUT: usize>
1239    CompiledExecutionCapability<E, C, F, BODY, OUTPUT>
1240where
1241    E: Fn(usize, C, ManagedBytes, CompiledDbPermit, ManagedResponseBuilder) -> F
1242        + Send
1243        + Sync
1244        + 'static,
1245    C: Send + Unpin + 'static,
1246    F: Future<Output = Result<ManagedResponse, ExecutionError>> + Send + 'static,
1247{
1248    pub fn lookup(
1249        &self,
1250        route_token: &[u8],
1251        framing: EntryFraming,
1252        declared_length: Option<u64>,
1253    ) -> Result<RegisteredRouteExecutionProof, RegistryError> {
1254        self.entries
1255            .lookup(route_token, framing, declared_length)
1256            .map(RegisteredEntry::execution_proof)
1257    }
1258
1259    fn execute(
1260        &self,
1261        proof: RegisteredRouteExecutionProof,
1262        context: C,
1263        body: ManagedBytes,
1264        db_permit: Option<DbRequestPermit>,
1265        memory: &RequestMemory,
1266    ) -> Result<F, ExecutionError> {
1267        let registered = RegisteredEntry::from_execution_proof(proof);
1268        let slot = self.entries.execution_slot(registered)?;
1269        if body.len() > proof.plan().body_hard_limit() as usize {
1270            return Err(ExecutionError::InputTooLarge);
1271        }
1272        let expected = proof.db_demand();
1273        let actual = db_permit.as_ref().map(DbRequestPermit::demand);
1274        let resources_match = match actual {
1275            None => expected.is_none(),
1276            Some(demand) => {
1277                u64::try_from(demand.connections()) == Ok(expected.connections())
1278                    && u64::try_from(demand.operations()) == Ok(expected.operations())
1279                    && !expected.is_none()
1280            }
1281        };
1282        if !resources_match {
1283            return Err(ExecutionError::RouteResourcesMismatch);
1284        }
1285        let response_capacity = usize::try_from(proof.response_capacity())
1286            .map_err(|_| ExecutionError::OutputTooLarge)?;
1287        if response_capacity != OUTPUT {
1288            return Err(ExecutionError::UnknownExecution);
1289        }
1290        let response = memory
1291            .try_response_builder(response_capacity)
1292            .map_err(|_| ExecutionError::ResponseReservationFailed)?;
1293        Ok((self.dispatcher)(
1294            slot,
1295            context,
1296            body,
1297            CompiledDbPermit {
1298                inner: db_permit,
1299                route_demand: expected,
1300                binding: self.db_bindings[slot],
1301            },
1302            response,
1303        ))
1304    }
1305}
1306
1307pin_project_lite::pin_project! {
1308    /// Fixed-shape classified successor around the generated concrete Future.
1309    ///
1310    /// The current-account error payload is reserved before the business
1311    /// Future is created and is carried across await without retaining
1312    /// `RequestMemory`.
1313    pub struct ClassifiedExecutionFuture<F> {
1314        #[pin]
1315        state: ClassifiedExecutionState<F>,
1316    }
1317}
1318
1319pin_project_lite::pin_project! {
1320    #[project = ClassifiedExecutionStateProj]
1321    enum ClassifiedExecutionState<F> {
1322        Running {
1323            #[pin]
1324            future: F,
1325            error_payload: Option<ManagedResponse>,
1326        },
1327        Ready {
1328            outcome: Option<saddle_runtime::compiled_route::CompiledResponseOutcome>,
1329        },
1330    }
1331}
1332
1333impl<F> ClassifiedExecutionFuture<F> {
1334    fn running(future: F, error_payload: ManagedResponse) -> Self {
1335        Self {
1336            state: ClassifiedExecutionState::Running {
1337                future,
1338                error_payload: Some(error_payload),
1339            },
1340        }
1341    }
1342
1343    fn ready(outcome: saddle_runtime::compiled_route::CompiledResponseOutcome) -> Self {
1344        Self {
1345            state: ClassifiedExecutionState::Ready {
1346                outcome: Some(outcome),
1347            },
1348        }
1349    }
1350}
1351
1352fn classified_outcome(
1353    class: saddle_runtime::compiled_route::ResponseOutcomeClass,
1354    payload: ManagedResponse,
1355) -> saddle_runtime::compiled_route::CompiledResponseOutcome {
1356    use saddle_runtime::compiled_route::{CompiledResponseOutcome, ResponseOutcomeClass};
1357
1358    match class {
1359        ResponseOutcomeClass::Success => CompiledResponseOutcome::success(payload),
1360        ResponseOutcomeClass::InvalidRequest => CompiledResponseOutcome::invalid_request(payload),
1361        ResponseOutcomeClass::BusinessRejected => {
1362            CompiledResponseOutcome::business_rejected(payload)
1363        }
1364        ResponseOutcomeClass::Unavailable => CompiledResponseOutcome::unavailable(payload),
1365        ResponseOutcomeClass::Internal => CompiledResponseOutcome::internal(payload),
1366    }
1367}
1368
1369impl<F> Future for ClassifiedExecutionFuture<F>
1370where
1371    F: Future<Output = Result<ManagedResponse, ExecutionError>>,
1372{
1373    type Output = saddle_runtime::compiled_route::CompiledResponseOutcome;
1374
1375    fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
1376        match self.project().state.project() {
1377            ClassifiedExecutionStateProj::Running {
1378                future,
1379                error_payload,
1380            } => match future.poll(context) {
1381                Poll::Pending => Poll::Pending,
1382                Poll::Ready(Ok(payload)) => Poll::Ready(
1383                    saddle_runtime::compiled_route::CompiledResponseOutcome::success(payload),
1384                ),
1385                Poll::Ready(Err(error)) => {
1386                    let payload = error_payload
1387                        .take()
1388                        .expect("classified execution future completes only once");
1389                    Poll::Ready(classified_outcome(error.response_outcome_class(), payload))
1390                }
1391            },
1392            ClassifiedExecutionStateProj::Ready { outcome } => Poll::Ready(
1393                outcome
1394                    .take()
1395                    .expect("classified execution future completes only once"),
1396            ),
1397        }
1398    }
1399}
1400
1401#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
1402impl<E, C, F, const BODY: usize, const OUTPUT: usize>
1403    saddle_runtime::compiled_route::CompiledRouteAdapter
1404    for CompiledExecutionCapability<E, C, F, BODY, OUTPUT>
1405where
1406    E: Fn(usize, C, ManagedBytes, CompiledDbPermit, ManagedResponseBuilder) -> F
1407        + Send
1408        + Sync
1409        + 'static,
1410    C: Send + Unpin + 'static,
1411    F: Future<Output = Result<ManagedResponse, ExecutionError>> + Send + 'static,
1412{
1413    type Proof = RegisteredRouteExecutionProof;
1414    type Context = C;
1415    type Error = ExecutionError;
1416    type Future = F;
1417
1418    fn managed_commitment(&self, proof: Self::Proof) -> Result<usize, Self::Error> {
1419        self.entries
1420            .execution_slot(RegisteredEntry::from_execution_proof(proof))?;
1421        usize::try_from(proof.plan().commitment()).map_err(|_| ExecutionError::OutputTooLarge)
1422    }
1423
1424    fn response_capacity(&self, proof: Self::Proof) -> Result<usize, Self::Error> {
1425        self.entries
1426            .execution_slot(RegisteredEntry::from_execution_proof(proof))?;
1427        usize::try_from(proof.response_capacity()).map_err(|_| ExecutionError::OutputTooLarge)
1428    }
1429
1430    fn db_resources<'a>(
1431        &self,
1432        proof: Self::Proof,
1433        domain: Option<&'a saddle_admission::DbPermitDomain>,
1434    ) -> Result<DbRouteResources<'a>, Self::Error> {
1435        self.entries
1436            .execution_slot(RegisteredEntry::from_execution_proof(proof))?;
1437        let demand = proof.db_demand();
1438        if demand.is_none() {
1439            return Ok(DbRouteResources::none());
1440        }
1441        let domain = domain.ok_or(ExecutionError::RouteResourcesMismatch)?;
1442        let connections =
1443            usize::try_from(demand.connections()).map_err(|_| ExecutionError::OutputTooLarge)?;
1444        let operations =
1445            usize::try_from(demand.operations()).map_err(|_| ExecutionError::OutputTooLarge)?;
1446        let demand = DbRouteCreditDemand::new(connections, operations)
1447            .map_err(|_| ExecutionError::RouteResourcesMismatch)?;
1448        Ok(DbRouteResources::required(domain, demand))
1449    }
1450
1451    fn execute(
1452        &self,
1453        proof: Self::Proof,
1454        context: Self::Context,
1455        body: ManagedBytes,
1456        permit: Option<DbRequestPermit>,
1457        memory: &RequestMemory,
1458    ) -> Result<Self::Future, Self::Error> {
1459        CompiledExecutionCapability::execute(self, proof, context, body, permit, memory)
1460    }
1461}
1462
1463#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
1464impl<E, C, F, const BODY: usize, const OUTPUT: usize>
1465    saddle_runtime::compiled_route::ClassifiedCompiledRouteAdapter
1466    for CompiledExecutionCapability<E, C, F, BODY, OUTPUT>
1467where
1468    E: Fn(usize, C, ManagedBytes, CompiledDbPermit, ManagedResponseBuilder) -> F
1469        + Send
1470        + Sync
1471        + 'static,
1472    C: Send + Unpin + 'static,
1473    F: Future<Output = Result<ManagedResponse, ExecutionError>> + Send + 'static,
1474{
1475    type Proof = RegisteredRouteExecutionProof;
1476    type Context = C;
1477    type Error = ExecutionError;
1478    type Future = ClassifiedExecutionFuture<F>;
1479
1480    fn managed_commitment(&self, proof: Self::Proof) -> Result<usize, Self::Error> {
1481        saddle_runtime::compiled_route::CompiledRouteAdapter::managed_commitment(self, proof)
1482    }
1483
1484    fn response_capacity(&self, proof: Self::Proof) -> Result<usize, Self::Error> {
1485        saddle_runtime::compiled_route::CompiledRouteAdapter::response_capacity(self, proof)
1486    }
1487
1488    fn db_resources<'a>(
1489        &self,
1490        proof: Self::Proof,
1491        domain: Option<&'a saddle_admission::DbPermitDomain>,
1492    ) -> Result<DbRouteResources<'a>, Self::Error> {
1493        saddle_runtime::compiled_route::CompiledRouteAdapter::db_resources(self, proof, domain)
1494    }
1495
1496    fn execute(
1497        &self,
1498        proof: Self::Proof,
1499        context: Self::Context,
1500        body: ManagedBytes,
1501        permit: Option<DbRequestPermit>,
1502        memory: &RequestMemory,
1503    ) -> Self::Future {
1504        let error_payload = memory
1505            .try_response(&[])
1506            .expect("zero-byte classified response is inside the current account commitment");
1507        match CompiledExecutionCapability::execute(self, proof, context, body, permit, memory) {
1508            Ok(future) => ClassifiedExecutionFuture::running(future, error_payload),
1509            Err(error) => ClassifiedExecutionFuture::ready(classified_outcome(
1510                error.response_outcome_class(),
1511                error_payload,
1512            )),
1513        }
1514    }
1515}
1516
1517/// The production adapter is the indivisible same-freeze bundle. Delegation
1518/// keeps the capacity leaf owned beside execution for the entire handoff.
1519#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
1520impl<E, C, F, const BODY: usize, const OUTPUT: usize>
1521    saddle_runtime::compiled_route::ClassifiedCompiledRouteAdapter
1522    for CompiledExecutionWithCapacityLeaf<E, C, F, BODY, OUTPUT>
1523where
1524    E: Fn(usize, C, ManagedBytes, CompiledDbPermit, ManagedResponseBuilder) -> F
1525        + Send
1526        + Sync
1527        + 'static,
1528    C: Send + Unpin + 'static,
1529    F: Future<Output = Result<ManagedResponse, ExecutionError>> + Send + 'static,
1530{
1531    type Proof = RegisteredRouteExecutionProof;
1532    type Context = C;
1533    type Error = ExecutionError;
1534    type Future = ClassifiedExecutionFuture<F>;
1535
1536    fn managed_commitment(&self, proof: Self::Proof) -> Result<usize, Self::Error> {
1537        saddle_runtime::compiled_route::ClassifiedCompiledRouteAdapter::managed_commitment(
1538            &self.execution,
1539            proof,
1540        )
1541    }
1542
1543    fn response_capacity(&self, proof: Self::Proof) -> Result<usize, Self::Error> {
1544        saddle_runtime::compiled_route::ClassifiedCompiledRouteAdapter::response_capacity(
1545            &self.execution,
1546            proof,
1547        )
1548    }
1549
1550    fn db_resources<'a>(
1551        &self,
1552        proof: Self::Proof,
1553        domain: Option<&'a saddle_admission::DbPermitDomain>,
1554    ) -> Result<DbRouteResources<'a>, Self::Error> {
1555        saddle_runtime::compiled_route::ClassifiedCompiledRouteAdapter::db_resources(
1556            &self.execution,
1557            proof,
1558            domain,
1559        )
1560    }
1561
1562    fn execute(
1563        &self,
1564        proof: Self::Proof,
1565        context: Self::Context,
1566        body: ManagedBytes,
1567        permit: Option<DbRequestPermit>,
1568        memory: &RequestMemory,
1569    ) -> Self::Future {
1570        saddle_runtime::compiled_route::ClassifiedCompiledRouteAdapter::execute(
1571            &self.execution,
1572            proof,
1573            context,
1574            body,
1575            permit,
1576            memory,
1577        )
1578    }
1579}
1580
1581#[cfg(test)]
1582mod tests {
1583    use std::{
1584        future::Future,
1585        pin::Pin,
1586        task::{Context, Poll},
1587    };
1588
1589    use saddle_core::Result as SaddleResult;
1590    use saddle_db::internal::{
1591        DbU64, ManagedQueryParameters, ManagedQueryRow, QueryOptionalOperationProof,
1592        StaticQueryOptionalOperation, StaticWriteOperation, TransactionDecision,
1593        TransactionOperationProof, WriteOperationProof,
1594    };
1595
1596    use super::*;
1597    use crate::{
1598        ServiceDescriptor,
1599        c4_entry::{EntryCapabilityLimits, EntryFraming},
1600    };
1601    use saddle_admission::{
1602        AdmissionError, DbCreditProfile, EntryIoAuditPlan, EntryReadPoll,
1603        OfficialTokioEntryIoAttemptOutcome, OfficialTokioRegistrationProfile,
1604        ProcessAllocationProfile, ProcessLedger, RequestMemory, ResourceConfig, ResponseWritePoll,
1605    };
1606    use saddle_runtime::compiled_route::{CompiledRouteAdapter, ResponseOutcomeClass};
1607
1608    const BODY: usize = 32;
1609    const OUTPUT: usize = 64;
1610    static EXECUTION_TEST_LOCK: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(());
1611
1612    fn request_ledger() -> (ProcessLedger, ProcessAllocationProfile) {
1613        let state = ProcessLedger::minimum_process_state_reserve(1).unwrap();
1614        let config = ResourceConfig {
1615            managed_capacity: 512,
1616            entry_reserve: 64,
1617            framework_reserve: 4_096,
1618            task_reserve: 4_096,
1619            process_state_reserve: state,
1620            system_estimate: 1_024,
1621            safety_margin: 512,
1622            process_limit: 512 + 64 + 4_096 + 4_096 + state + 1_024 + 512,
1623            max_active_requests: 1,
1624        };
1625        let ledger = ProcessLedger::new(config).unwrap();
1626        let allocation = ledger
1627            .prepare_process_allocation_profile(usize::MAX)
1628            .unwrap();
1629        (ledger, allocation)
1630    }
1631
1632    fn entry_plan() -> EntryIoAuditPlan {
1633        EntryIoAuditPlan::locked_linux_x86_64_tokio_1_53_1(64, 64, &[]).unwrap()
1634    }
1635
1636    fn official_ledger() -> (
1637        ProcessLedger,
1638        saddle_admission::OfficialTokioDomain,
1639        ProcessAllocationProfile,
1640    ) {
1641        let registration = OfficialTokioRegistrationProfile {
1642            listener: 1,
1643            transport_connections: 1,
1644            runtime_fixed: 1,
1645        };
1646        let state = ProcessLedger::minimum_process_state_reserve(1).unwrap()
1647            + ProcessLedger::official_tokio_state_reserve(registration).unwrap();
1648        let config = ResourceConfig {
1649            managed_capacity: 512,
1650            entry_reserve: 64,
1651            framework_reserve: 4_096,
1652            task_reserve: 4_096,
1653            process_state_reserve: state,
1654            system_estimate: 1_024,
1655            safety_margin: 512,
1656            process_limit: 512 + 64 + 4_096 + 4_096 + state + 1_024 + 512,
1657            max_active_requests: 1,
1658        };
1659        let ledger = ProcessLedger::new(config).unwrap();
1660        let domain = ledger.prepare_official_tokio_domain(registration).unwrap();
1661        let allocation = ledger
1662            .prepare_process_allocation_profile(usize::MAX)
1663            .unwrap();
1664        (ledger, domain, allocation)
1665    }
1666
1667    struct TestConnection;
1668
1669    struct UserRead;
1670
1671    impl EntryReadPoll<TestConnection> for UserRead {
1672        fn poll_read(
1673            &mut self,
1674            _: &mut TestConnection,
1675            memory: &RequestMemory,
1676            _: &mut Context<'_>,
1677        ) -> Poll<Result<ManagedBytes, AdmissionError>> {
1678            Poll::Ready(memory.try_bytes(&42_u64.to_le_bytes()))
1679        }
1680    }
1681
1682    struct UserWrite;
1683
1684    impl ResponseWritePoll<TestConnection> for UserWrite {
1685        fn poll_write(
1686            &mut self,
1687            _: &mut TestConnection,
1688            response: &ManagedResponse,
1689            _: &mut Context<'_>,
1690        ) -> Poll<Result<(), AdmissionError>> {
1691            assert_eq!(
1692                u64::from_le_bytes(response.as_slice()[..8].try_into().unwrap()),
1693                45
1694            );
1695            assert_eq!(response.as_slice()[8], 1);
1696            Poll::Ready(Ok(()))
1697        }
1698    }
1699
1700    struct UserLookup;
1701
1702    impl Service for UserLookup {
1703        type Request = UserRequest;
1704        type Response = UserResponse;
1705    }
1706
1707    struct OrderCreate;
1708
1709    impl Service for OrderCreate {
1710        type Request = OrderRequest;
1711        type Response = OrderResponse;
1712    }
1713
1714    #[derive(Clone, Copy)]
1715    struct RequestIdentity(u64);
1716
1717    type UserRequest = ManagedU64;
1718    type UserResponse = ManagedPair<ManagedU64, ManagedBool>;
1719    type UserCodec = FixedManagedCodec<UserRequest, UserResponse>;
1720    type OrderRequest = ManagedPair<ManagedU64, ManagedU64>;
1721    type OrderResponse = ManagedPair<ManagedU64, ManagedU64>;
1722    type OrderCodec = FixedManagedCodec<OrderRequest, OrderResponse>;
1723
1724    struct SelectUser;
1725
1726    impl StaticQueryOptionalOperation for SelectUser {
1727        type Parameters = ManagedQueryParameters<DbU64>;
1728        type Row = ManagedQueryRow<DbU64>;
1729
1730        const OPERATION: &'static str = "users.lookup";
1731        const SQL: &'static str = "SELECT id FROM users WHERE id = ?";
1732    }
1733
1734    struct SelectOrder;
1735
1736    impl StaticQueryOptionalOperation for SelectOrder {
1737        type Parameters = ManagedQueryParameters<DbU64>;
1738        type Row = ManagedQueryRow<DbU64>;
1739
1740        const OPERATION: &'static str = "orders.lookup";
1741        const SQL: &'static str = "SELECT id FROM orders WHERE id = ?";
1742    }
1743
1744    struct UpdateUser;
1745
1746    impl StaticWriteOperation for UpdateUser {
1747        type Parameters = ManagedQueryParameters<DbU64>;
1748
1749        const OPERATION: &'static str = "users.update";
1750        const SQL: &'static str = "UPDATE users SET active = 1 WHERE id = ?";
1751    }
1752
1753    struct ReadyOnce<T> {
1754        value: Option<T>,
1755        yielded: bool,
1756    }
1757
1758    impl<T> ReadyOnce<T> {
1759        fn new(value: T) -> Self {
1760            Self {
1761                value: Some(value),
1762                yielded: false,
1763            }
1764        }
1765    }
1766
1767    impl<T: Unpin> Future for ReadyOnce<T> {
1768        type Output = T;
1769
1770        fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<T> {
1771            if !self.yielded {
1772                self.yielded = true;
1773                context.waker().wake_by_ref();
1774                return Poll::Pending;
1775            }
1776            Poll::Ready(self.value.take().expect("future completes only once"))
1777        }
1778    }
1779
1780    fn user_handler(
1781        identity: RequestIdentity,
1782        request: UserRequest,
1783    ) -> ReadyOnce<Result<UserResponse, ExecutionError>> {
1784        ReadyOnce::new(Ok(ManagedPair(
1785            ManagedU64(request.0 ^ identity.0),
1786            ManagedBool(true),
1787        )))
1788    }
1789
1790    fn order_handler(
1791        identity: RequestIdentity,
1792        request: OrderRequest,
1793    ) -> ReadyOnce<Result<OrderResponse, ExecutionError>> {
1794        ReadyOnce::new(Ok(ManagedPair(
1795            ManagedU64(request.0.0 ^ identity.0),
1796            request.1,
1797        )))
1798    }
1799
1800    fn generated_layout(slot: usize) -> Option<CompiledRouteExecutionProof> {
1801        match slot {
1802            0 => ExecutionLayoutProof::bind::<
1803                UserLookup,
1804                BODY,
1805                OUTPUT,
1806                UserRequest,
1807                UserResponse,
1808                UserCodec,
1809                ReadyOnce<Result<UserResponse, ExecutionError>>,
1810            >()
1811            .ok()
1812            .map(|layout| {
1813                layout.with_query_optional(
1814                    QueryOptionalOperationProof::<SelectUser>::bind()
1815                        .expect("generated DB operation is valid"),
1816                )
1817            }),
1818            1 => ExecutionLayoutProof::bind::<
1819                OrderCreate,
1820                BODY,
1821                OUTPUT,
1822                OrderRequest,
1823                OrderResponse,
1824                OrderCodec,
1825                ReadyOnce<Result<OrderResponse, ExecutionError>>,
1826            >()
1827            .ok()
1828            .map(ExecutionLayoutProof::without_db),
1829            _ => None,
1830        }
1831    }
1832
1833    // Rust 1.85 precise capture is the contract under test; `async fn` would
1834    // capture all argument lifetimes once Runtime resources are added.
1835    #[allow(clippy::manual_async_fn)]
1836    fn generated_dispatch(
1837        slot: usize,
1838        identity: RequestIdentity,
1839        body: ManagedBytes,
1840        db: CompiledDbPermit,
1841        mut output: ManagedResponseBuilder,
1842    ) -> impl Future<Output = Result<ManagedResponse, ExecutionError>> + Send + 'static + use<>
1843    {
1844        enum Decoded {
1845            User(UserRequest),
1846            Order(OrderRequest),
1847        }
1848        let decoded = match slot {
1849            0 => body
1850                .as_slice()
1851                .try_into()
1852                .map(u64::from_le_bytes)
1853                .map(ManagedU64)
1854                .map(Decoded::User)
1855                .map_err(|_| ExecutionError::InvalidInput),
1856            1 => body
1857                .as_slice()
1858                .try_into()
1859                .map(|bytes: [u8; 16]| {
1860                    Decoded::Order(ManagedPair(
1861                        ManagedU64(u64::from_le_bytes(bytes[..8].try_into().unwrap())),
1862                        ManagedU64(u64::from_le_bytes(bytes[8..].try_into().unwrap())),
1863                    ))
1864                })
1865                .map_err(|_| ExecutionError::InvalidInput),
1866            _ => Err(ExecutionError::UnknownExecution),
1867        };
1868        drop(body);
1869        async move {
1870            match decoded? {
1871                Decoded::User(request) => {
1872                    assert_eq!(db.demand(), Some((1, 1)));
1873                    let response = user_handler(identity, request).await?;
1874                    // This generated fixture has no physical database connection.
1875                    // Model its completed return explicitly so the real admission
1876                    // role is not dropped while still in Query.
1877                    db.complete_after_test_connection_return();
1878                    output
1879                        .try_extend_from_slice(&response.0.0.to_le_bytes())
1880                        .map_err(|_| ExecutionError::OutputTooLarge)?;
1881                    output
1882                        .try_extend_from_slice(&[u8::from(response.1.0)])
1883                        .map_err(|_| ExecutionError::OutputTooLarge)?;
1884                }
1885                Decoded::Order(request) => {
1886                    assert!(db.is_none());
1887                    let query = QueryOptionalOperationProof::<SelectUser>::bind()
1888                        .expect("generated DB operation is valid");
1889                    assert!(matches!(
1890                        db.handoff_query_optional(
1891                            query.invocation(ManagedQueryParameters(DbU64(1)))
1892                        ),
1893                        Err(ExecutionError::RouteResourcesMismatch)
1894                    ));
1895                    let response = order_handler(identity, request).await?;
1896                    output
1897                        .try_extend_from_slice(&response.0.0.to_le_bytes())
1898                        .map_err(|_| ExecutionError::OutputTooLarge)?;
1899                    output
1900                        .try_extend_from_slice(&response.1.0.to_le_bytes())
1901                        .map_err(|_| ExecutionError::OutputTooLarge)?;
1902                }
1903            }
1904            output
1905                .finish()
1906                .map_err(|_| ExecutionError::ResponseReservationFailed)
1907        }
1908    }
1909
1910    fn registry() -> CompiledServiceRegistry {
1911        let mut builder = CompiledServiceRegistryBuilder::new(2).unwrap();
1912        builder
1913            .register::<UserLookup>(ServiceDescriptor::new("users", "user", "lookup"))
1914            .unwrap();
1915        builder
1916            .register::<OrderCreate>(ServiceDescriptor::new("orders", "order", "create"))
1917            .unwrap();
1918        builder.freeze().unwrap()
1919    }
1920
1921    macro_rules! capability {
1922        () => {{
1923            let mut builder = CompiledExecutionBuilder::<_, _, _, _, BODY, OUTPUT>::new(
1924                registry(),
1925                generated_dispatch,
1926                generated_layout,
1927                EntryCapabilityLimits::new(2, 32, 64, 2, 2),
1928            )
1929            .unwrap();
1930            builder
1931                .expose::<UserLookup>(
1932                    b"/users/lookup",
1933                    EntryFraming::post_managed_content_length(),
1934                )
1935                .unwrap();
1936            builder
1937                .expose::<OrderCreate>(
1938                    b"/orders/create",
1939                    EntryFraming::post_managed_content_length(),
1940                )
1941                .unwrap();
1942            builder.freeze().unwrap()
1943        }};
1944    }
1945
1946    struct TestCapacityArtifact;
1947
1948    impl GeneratedServiceCapacityArtifactOwner for TestCapacityArtifact {
1949        fn build_identity(&self) -> [u8; 32] {
1950            [0xb1; 32]
1951        }
1952
1953        fn artifact_identity(&self) -> [u8; 32] {
1954            [0xa1; 32]
1955        }
1956
1957        fn owner_generation(&self) -> u64 {
1958            7
1959        }
1960    }
1961
1962    #[test]
1963    fn capacity_leaf_is_frozen_with_the_execution_registry() {
1964        use saddle_admission::ServiceCapacitySourceLeaf;
1965
1966        let mut builder = CompiledExecutionBuilder::<_, _, _, _, BODY, OUTPUT>::new(
1967            registry(),
1968            generated_dispatch,
1969            generated_layout,
1970            EntryCapabilityLimits::new(2, 32, 64, 2, 2),
1971        )
1972        .unwrap();
1973        builder
1974            .expose::<UserLookup>(
1975                b"/users/lookup",
1976                EntryFraming::post_managed_content_length(),
1977            )
1978            .unwrap();
1979        builder
1980            .expose::<OrderCreate>(
1981                b"/orders/create",
1982                EntryFraming::post_managed_content_length(),
1983            )
1984            .unwrap();
1985        let execution = builder
1986            .freeze_with_capacity_leaf(TestCapacityArtifact)
1987            .unwrap();
1988        let leaf = execution.capacity_source();
1989
1990        assert_eq!(leaf.owner_generation(), 7);
1991        assert_eq!(leaf.route_count(), 2);
1992        assert_eq!(leaf.managed_objects_peak(0), Some(2));
1993        assert_eq!(leaf.managed_objects_peak(1), Some(2));
1994        assert_ne!(leaf.leaf_identity(), [0; 32]);
1995        assert_ne!(leaf.route_type_closure_identity(), [0; 32]);
1996        assert_eq!(leaf.common_identities()[..2], [[0xb1; 32], [0xa1; 32]]);
1997        assert_ne!(leaf.common_identities()[2], [0; 32]);
1998
1999        for index in 0..leaf.route_count() {
2000            let route = leaf.route_identity(index).unwrap();
2001            assert_ne!(route, 0);
2002            assert_eq!(route >> 32, leaf.route_identity(0).unwrap() >> 32);
2003            assert!(leaf.managed_commitment_bytes(index).unwrap() > 0);
2004        }
2005        assert_eq!(leaf.db_connections(0), Some(1));
2006        assert_eq!(leaf.db_operations(0), Some(1));
2007        assert_eq!(leaf.db_connections(1), Some(0));
2008        assert_eq!(leaf.db_operations(1), Some(0));
2009
2010        let query = execution
2011            .lookup(
2012                b"/users/lookup",
2013                EntryFraming::post_managed_content_length(),
2014                Some(8),
2015            )
2016            .unwrap();
2017        let no_db = execution
2018            .lookup(
2019                b"/orders/create",
2020                EntryFraming::post_managed_content_length(),
2021                Some(16),
2022            )
2023            .unwrap();
2024        assert_eq!(
2025            query.identity().contract().opaque(),
2026            leaf.route_identity(0).unwrap()
2027        );
2028        assert_eq!(
2029            no_db.identity().contract().opaque(),
2030            leaf.route_identity(1).unwrap()
2031        );
2032        assert_eq!(
2033            query.plan().commitment() as usize,
2034            leaf.managed_commitment_bytes(0).unwrap()
2035        );
2036        assert_eq!(
2037            no_db.plan().commitment() as usize,
2038            leaf.managed_commitment_bytes(1).unwrap()
2039        );
2040
2041        let first = execution
2042            .production_fact_input()
2043            .unwrap()
2044            .into_canonical_json()
2045            .unwrap();
2046        let second = execution
2047            .production_fact_input()
2048            .unwrap()
2049            .into_canonical_json()
2050            .unwrap();
2051        assert_eq!(first, second);
2052        assert!(!first.contains(&b'\n'));
2053        let document: serde_json::Value = serde_json::from_slice(&first).unwrap();
2054        assert_eq!(document["authority"], false);
2055        assert_eq!(document["capacity"]["manifest_leaf"], "service");
2056        assert_eq!(document["capacity"]["commitment"], "service_capacity");
2057        assert_eq!(document["capacity"]["routes"][0]["route"], "/orders/create");
2058        assert_eq!(document["capacity"]["routes"][0]["db_connections"], 0);
2059        assert_eq!(document["capacity"]["routes"][1]["route"], "/users/lookup");
2060        assert_eq!(document["capacity"]["routes"][1]["db_connections"], 1);
2061        assert!(document.get("root_identity").is_none());
2062        assert!(document.get("owner_generation").is_none());
2063    }
2064
2065    #[tokio::test(flavor = "current_thread")]
2066    async fn factory_reserves_response_and_real_async_path_finishes_after_await() {
2067        let _serial = EXECUTION_TEST_LOCK.lock().await;
2068        let capability = capability!();
2069        let order = capability
2070            .lookup(
2071                b"/orders/create",
2072                EntryFraming::post_managed_content_length(),
2073                Some(16),
2074            )
2075            .unwrap();
2076        fn assert_runtime_adapter<A: CompiledRouteAdapter>(_: &A) {}
2077        assert_runtime_adapter(&capability);
2078        assert_eq!(
2079            capability.managed_commitment(order).unwrap(),
2080            usize::try_from(order.plan().commitment()).unwrap()
2081        );
2082        assert_eq!(capability.response_capacity(order).unwrap(), OUTPUT);
2083        assert!(capability.db_resources(order, None).is_ok());
2084        assert!(order.db_demand().is_none());
2085        assert_eq!(order.response_capacity(), OUTPUT as u64);
2086        let (ledger, allocation) = request_ledger();
2087        let envelope = ledger
2088            .try_envelope(256, 2_048, |memory| {
2089                let mut bytes = [0_u8; 16];
2090                bytes[..8].copy_from_slice(&42_u64.to_le_bytes());
2091                bytes[8..].copy_from_slice(&1999_u64.to_le_bytes());
2092                let body = memory.try_bytes(&bytes).unwrap();
2093                let execution = CompiledRouteAdapter::execute(
2094                    &capability,
2095                    order,
2096                    RequestIdentity(9),
2097                    body,
2098                    None,
2099                    memory,
2100                )
2101                .unwrap();
2102                async move {
2103                    let response = execution.await.unwrap();
2104                    assert_eq!(
2105                        u64::from_le_bytes(response.as_slice()[..8].try_into().unwrap()),
2106                        35
2107                    );
2108                    assert_eq!(
2109                        u64::from_le_bytes(response.as_slice()[8..].try_into().unwrap()),
2110                        1999
2111                    );
2112                }
2113            })
2114            .unwrap();
2115        let report = envelope.await.unwrap();
2116        assert_eq!(report.escape_allocations, 0);
2117        assert_eq!(report.managed_allocations, 2);
2118        allocation.finish().unwrap();
2119        ledger.try_shutdown().unwrap();
2120    }
2121
2122    #[tokio::test(flavor = "current_thread")]
2123    async fn sealed_db_permit_and_response_capacity_cross_the_real_async_path() {
2124        let _serial = EXECUTION_TEST_LOCK.lock().await;
2125        let capability = capability!();
2126        let user = capability
2127            .lookup(
2128                b"/users/lookup",
2129                EntryFraming::post_managed_content_length(),
2130                Some(8),
2131            )
2132            .unwrap();
2133        let proof = user;
2134        assert_eq!(proof.db_demand().connections(), 1);
2135        assert_eq!(proof.db_demand().operations(), 1);
2136        assert_eq!(proof.response_capacity(), OUTPUT as u64);
2137
2138        let (ledger, domain, allocation) = official_ledger();
2139        let db = ledger
2140            .prepare_db_domain(DbCreditProfile {
2141                connections: 1,
2142                operations: 1,
2143            })
2144            .unwrap();
2145        let resources = capability.db_resources(proof, Some(&db)).unwrap();
2146        let envelope = match ledger.attempt_official_tokio_entry_io(
2147            &domain,
2148            resources,
2149            256,
2150            4_096,
2151            entry_plan(),
2152            entry_plan(),
2153            |_| (TestConnection, UserRead, UserWrite),
2154            |body, permit, memory| {
2155                let execution = CompiledRouteAdapter::execute(
2156                    &capability,
2157                    user,
2158                    RequestIdentity(7),
2159                    body,
2160                    permit,
2161                    memory,
2162                )
2163                .unwrap();
2164                fn assert_send_static<T: Send + 'static>(_: &T) {}
2165                assert_send_static(&execution);
2166                async move { execution.await.unwrap() }
2167            },
2168        ) {
2169            OfficialTokioEntryIoAttemptOutcome::Ready(envelope) => envelope,
2170            _ => panic!("sealed route resources must admit"),
2171        };
2172        let (envelope, task_slot) = envelope.into_runtime_parts();
2173        let report = envelope.await.unwrap();
2174        drop(task_slot);
2175        assert_eq!(report.escape_allocations, 0);
2176        assert_eq!(db.snapshot().unwrap().connections_in_use, 0);
2177        drop(db);
2178        drop(domain);
2179        allocation.finish().unwrap();
2180        ledger.try_shutdown().unwrap();
2181    }
2182
2183    #[tokio::test(flavor = "current_thread")]
2184    async fn missing_db_permit_fails_before_response_reservation_or_dispatch() {
2185        let _serial = EXECUTION_TEST_LOCK.lock().await;
2186        let capability = capability!();
2187        let user = capability
2188            .lookup(
2189                b"/users/lookup",
2190                EntryFraming::post_managed_content_length(),
2191                Some(8),
2192            )
2193            .unwrap();
2194        let (ledger, allocation) = request_ledger();
2195        let envelope = ledger
2196            .try_envelope(256, 2_048, |memory| {
2197                let body = memory.try_bytes(&42_u64.to_le_bytes()).unwrap();
2198                assert!(matches!(
2199                    capability.execute(user, RequestIdentity(7), body, None, memory),
2200                    Err(ExecutionError::RouteResourcesMismatch)
2201                ));
2202                std::future::ready(())
2203            })
2204            .unwrap();
2205        let report = envelope.await.unwrap();
2206        assert_eq!(report.managed_allocations, 1);
2207        allocation.finish().unwrap();
2208        ledger.try_shutdown().unwrap();
2209    }
2210
2211    #[tokio::test(flavor = "current_thread")]
2212    async fn registered_entry_is_the_only_execution_credential() {
2213        let _serial = EXECUTION_TEST_LOCK.lock().await;
2214        let first = capability!();
2215        let second = capability!();
2216        let foreign = first
2217            .lookup(
2218                b"/users/lookup",
2219                EntryFraming::post_managed_content_length(),
2220                Some(8),
2221            )
2222            .unwrap();
2223        assert!(matches!(
2224            CompiledRouteAdapter::managed_commitment(&second, foreign),
2225            Err(ExecutionError::Registry(RegistryError::UnknownFactory))
2226        ));
2227        assert!(matches!(
2228            second.db_resources(foreign, None),
2229            Err(ExecutionError::Registry(RegistryError::UnknownFactory))
2230        ));
2231        let (ledger, allocation) = request_ledger();
2232        let envelope = ledger
2233            .try_envelope(256, 2_048, |memory| {
2234                let body = memory.try_bytes(&1_u64.to_le_bytes()).unwrap();
2235                let result = second.execute(foreign, RequestIdentity(1), body, None, memory);
2236                assert!(matches!(
2237                    result,
2238                    Err(ExecutionError::Registry(RegistryError::UnknownFactory))
2239                ));
2240                std::future::ready(())
2241            })
2242            .unwrap();
2243        envelope.await.unwrap();
2244        allocation.finish().unwrap();
2245        ledger.try_shutdown().unwrap();
2246    }
2247
2248    #[test]
2249    fn proof_is_derived_from_concrete_layouts_and_fixed_payloads_do_not_allocate() {
2250        type Handler = ReadyOnce<Result<OrderResponse, ExecutionError>>;
2251        let proof = ExecutionLayoutProof::bind::<
2252            OrderCreate,
2253            BODY,
2254            OUTPUT,
2255            OrderRequest,
2256            OrderResponse,
2257            OrderCodec,
2258            Handler,
2259        >()
2260        .unwrap();
2261        assert_eq!(proof.body_bytes(), BODY);
2262        assert_eq!(proof.future_bytes(), mem::size_of::<Handler>());
2263        assert!(proof.commitment().unwrap() >= BODY + OUTPUT);
2264        assert_eq!(FixedBytes::<BODY>::empty().capacity(), BODY);
2265    }
2266
2267    #[test]
2268    fn marker_binding_and_typed_internal_edges_are_checked_at_assembly() {
2269        struct Missing;
2270        impl Service for Missing {
2271            type Request = ManagedU64;
2272            type Response = ManagedU64;
2273        }
2274
2275        let mut builder = CompiledExecutionBuilder::<_, _, _, _, BODY, OUTPUT>::new(
2276            registry(),
2277            generated_dispatch,
2278            generated_layout,
2279            EntryCapabilityLimits::new(2, 32, 64, 2, 2),
2280        )
2281        .unwrap();
2282        let edge = builder
2283            .bind_internal_call::<UserLookup, OrderCreate>()
2284            .unwrap();
2285        let _: InternalCallCapability<UserLookup, OrderCreate> = edge;
2286        assert!(matches!(
2287            builder.bind_internal_call::<UserLookup, Missing>(),
2288            Err(ExecutionError::Registry(RegistryError::UnregisteredService))
2289        ));
2290
2291        let order_proof = generated_layout(1).unwrap();
2292        let mut mismatched = CompiledExecutionBuilder::<_, _, _, _, BODY, OUTPUT>::new(
2293            registry(),
2294            generated_dispatch,
2295            move |_| Some(order_proof),
2296            EntryCapabilityLimits::new(2, 32, 64, 2, 2),
2297        )
2298        .unwrap();
2299        assert_eq!(
2300            mismatched
2301                .expose::<UserLookup>(
2302                    b"/users/lookup",
2303                    EntryFraming::post_managed_content_length(),
2304                )
2305                .unwrap_err(),
2306            ExecutionError::UnknownExecution
2307        );
2308
2309        builder
2310            .expose::<UserLookup>(
2311                b"/users/lookup",
2312                EntryFraming::post_managed_content_length(),
2313            )
2314            .unwrap();
2315    }
2316
2317    #[test]
2318    fn assembly_rejects_foreign_query_proof_for_one_route() {
2319        let conflicting_layout = |slot| {
2320            generated_layout(slot).map(|proof| {
2321                if slot == 0 {
2322                    proof.with_query_optional(
2323                        QueryOptionalOperationProof::<SelectOrder>::bind().unwrap(),
2324                    )
2325                } else {
2326                    proof
2327                }
2328            })
2329        };
2330        let mut builder = CompiledExecutionBuilder::<_, _, _, _, BODY, OUTPUT>::new(
2331            registry(),
2332            generated_dispatch,
2333            conflicting_layout,
2334            EntryCapabilityLimits::new(2, 32, 64, 2, 2),
2335        )
2336        .unwrap();
2337        assert_eq!(
2338            builder
2339                .expose::<UserLookup>(
2340                    b"/users/lookup",
2341                    EntryFraming::post_managed_content_length(),
2342                )
2343                .unwrap_err(),
2344            ExecutionError::UnknownExecution
2345        );
2346    }
2347
2348    #[test]
2349    fn assembly_freezes_standalone_write_and_single_step_transaction_kinds() {
2350        let write_layout = |slot| {
2351            if slot != 0 {
2352                return generated_layout(slot);
2353            }
2354            ExecutionLayoutProof::bind::<
2355                UserLookup,
2356                BODY,
2357                OUTPUT,
2358                UserRequest,
2359                UserResponse,
2360                UserCodec,
2361                ReadyOnce<Result<UserResponse, ExecutionError>>,
2362            >()
2363            .ok()
2364            .map(|layout| layout.with_write(WriteOperationProof::<UpdateUser>::bind().unwrap()))
2365        };
2366        let mut write = CompiledExecutionBuilder::<_, _, _, _, BODY, OUTPUT>::new(
2367            registry(),
2368            generated_dispatch,
2369            write_layout,
2370            EntryCapabilityLimits::new(2, 32, 64, 2, 2),
2371        )
2372        .unwrap();
2373        write
2374            .expose::<UserLookup>(
2375                b"/users/update",
2376                EntryFraming::post_managed_content_length(),
2377            )
2378            .unwrap();
2379        let _write_capability = write.freeze().unwrap();
2380
2381        let transaction_layout = |slot| {
2382            if slot != 0 {
2383                return generated_layout(slot);
2384            }
2385            ExecutionLayoutProof::bind::<
2386                UserLookup,
2387                BODY,
2388                OUTPUT,
2389                UserRequest,
2390                UserResponse,
2391                UserCodec,
2392                ReadyOnce<Result<UserResponse, ExecutionError>>,
2393            >()
2394            .ok()
2395            .map(|layout| {
2396                layout.with_transaction(TransactionOperationProof::<UpdateUser>::bind().unwrap())
2397            })
2398        };
2399        let mut transaction = CompiledExecutionBuilder::<_, _, _, _, BODY, OUTPUT>::new(
2400            registry(),
2401            generated_dispatch,
2402            transaction_layout,
2403            EntryCapabilityLimits::new(2, 32, 64, 2, 2),
2404        )
2405        .unwrap();
2406        transaction
2407            .expose::<UserLookup>(
2408                b"/users/transaction",
2409                EntryFraming::post_managed_content_length(),
2410            )
2411            .unwrap();
2412        let _transaction_capability = transaction.freeze().unwrap();
2413    }
2414
2415    #[test]
2416    fn assembly_rejects_write_transaction_value_recombination() {
2417        let recombined = |slot| {
2418            if slot != 0 {
2419                return generated_layout(slot);
2420            }
2421            ExecutionLayoutProof::bind::<
2422                UserLookup,
2423                BODY,
2424                OUTPUT,
2425                UserRequest,
2426                UserResponse,
2427                UserCodec,
2428                ReadyOnce<Result<UserResponse, ExecutionError>>,
2429            >()
2430            .ok()
2431            .map(|layout| {
2432                layout
2433                    .with_write(WriteOperationProof::<UpdateUser>::bind().unwrap())
2434                    .with_transaction(TransactionOperationProof::<UpdateUser>::bind().unwrap())
2435            })
2436        };
2437        let mut builder = CompiledExecutionBuilder::<_, _, _, _, BODY, OUTPUT>::new(
2438            registry(),
2439            generated_dispatch,
2440            recombined,
2441            EntryCapabilityLimits::new(2, 32, 64, 2, 2),
2442        )
2443        .unwrap();
2444        assert_eq!(
2445            builder
2446                .expose::<UserLookup>(
2447                    b"/users/recombined",
2448                    EntryFraming::post_managed_content_length(),
2449                )
2450                .unwrap_err(),
2451            ExecutionError::UnknownExecution
2452        );
2453
2454        let transaction = TransactionOperationProof::<UpdateUser>::bind()
2455            .unwrap()
2456            .invocation(
2457                ManagedQueryParameters(DbU64(1)),
2458                TransactionDecision::Commit,
2459            );
2460        assert_eq!(
2461            transaction.layout().parameter_bytes(),
2462            mem::size_of::<ManagedQueryParameters<DbU64>>()
2463        );
2464    }
2465
2466    #[test]
2467    fn compiled_registry_is_bounded_and_rejects_duplicate_markers_and_identities() {
2468        assert!(matches!(
2469            CompiledServiceRegistryBuilder::new(0),
2470            Err(CompiledRegistryError::InvalidCapacity)
2471        ));
2472        assert!(matches!(
2473            CompiledServiceRegistryBuilder::new(1).unwrap().freeze(),
2474            Err(CompiledRegistryError::EmptyRegistry)
2475        ));
2476
2477        let mut duplicate_contract = CompiledServiceRegistryBuilder::new(2).unwrap();
2478        duplicate_contract
2479            .register::<UserLookup>(ServiceDescriptor::new("users", "user", "lookup"))
2480            .unwrap();
2481        assert_eq!(
2482            duplicate_contract
2483                .register::<UserLookup>(ServiceDescriptor::new("users", "user", "other"))
2484                .unwrap_err(),
2485            CompiledRegistryError::DuplicateContract
2486        );
2487
2488        let mut duplicate_identity = CompiledServiceRegistryBuilder::new(2).unwrap();
2489        duplicate_identity
2490            .register::<UserLookup>(ServiceDescriptor::new("users", "user", "lookup"))
2491            .unwrap();
2492        assert_eq!(
2493            duplicate_identity
2494                .register::<OrderCreate>(ServiceDescriptor::new("users", "user", "lookup"))
2495                .unwrap_err(),
2496            CompiledRegistryError::DuplicateIdentity
2497        );
2498
2499        let mut bounded = CompiledServiceRegistryBuilder::new(1).unwrap();
2500        bounded
2501            .register::<UserLookup>(ServiceDescriptor::new("users", "user", "lookup"))
2502            .unwrap();
2503        assert_eq!(
2504            bounded
2505                .register::<OrderCreate>(ServiceDescriptor::new("orders", "order", "create"))
2506                .unwrap_err(),
2507            CompiledRegistryError::CapacityExceeded
2508        );
2509    }
2510
2511    #[test]
2512    fn execution_errors_exhaustively_map_to_the_closed_response_classes() {
2513        for error in [ExecutionError::InputTooLarge, ExecutionError::InvalidInput] {
2514            assert_eq!(
2515                error.response_outcome_class(),
2516                ResponseOutcomeClass::InvalidRequest
2517            );
2518        }
2519        assert_eq!(
2520            ExecutionError::Business.response_outcome_class(),
2521            ResponseOutcomeClass::BusinessRejected
2522        );
2523        assert_eq!(
2524            ExecutionError::RouteResourcesMismatch.response_outcome_class(),
2525            ResponseOutcomeClass::Unavailable
2526        );
2527        assert_eq!(
2528            ExecutionError::DependencyUnavailable.response_outcome_class(),
2529            ResponseOutcomeClass::Unavailable
2530        );
2531        assert_eq!(
2532            ExecutionError::DependencyExecutionFailed.response_outcome_class(),
2533            ResponseOutcomeClass::Internal
2534        );
2535        for error in [
2536            ExecutionError::Registry(RegistryError::UnknownFactory),
2537            ExecutionError::CompiledRegistry(CompiledRegistryError::EmptyRegistry),
2538            ExecutionError::UnknownExecution,
2539            ExecutionError::OutputTooLarge,
2540            ExecutionError::ResponseReservationFailed,
2541        ] {
2542            assert_eq!(
2543                error.response_outcome_class(),
2544                ResponseOutcomeClass::Internal
2545            );
2546        }
2547    }
2548
2549    #[test]
2550    fn frozen_capability_is_the_classified_production_adapter() {
2551        fn assert_classified<A>(_: &A)
2552        where
2553            A: saddle_runtime::compiled_route::ClassifiedCompiledRouteAdapter<
2554                    Proof = RegisteredRouteExecutionProof,
2555                    Context = RequestIdentity,
2556                    Error = ExecutionError,
2557                >,
2558        {
2559        }
2560
2561        let capability = capability!();
2562        assert_classified(&capability);
2563    }
2564
2565    #[allow(dead_code)]
2566    fn _assert_context_is_owned_and_send(value: RequestIdentity) -> SaddleResult<()> {
2567        fn assert_send_static<T: Send + 'static>(_: T) {}
2568        assert_send_static(value);
2569        Ok(())
2570    }
2571}