lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

signer.rs (3627B)


      1 //! Object-safe signer service-provider interface.
      2 
      3 use core::{future::Future, pin::Pin};
      4 
      5 #[cfg(not(feature = "std"))]
      6 use alloc::{boxed::Box, sync::Arc};
      7 #[cfg(feature = "std")]
      8 use std::{boxed::Box, sync::Arc};
      9 
     10 use crate::{
     11     AuthoredSignEvidence, Error, SignReceipt, SignRequest, SignerStatus, error::Kind,
     12     receipt::verify_identity,
     13 };
     14 
     15 pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
     16 
     17 /// Protocol-neutral, caller-driven signing service-provider interface.
     18 ///
     19 /// Implementations must document their durable remote-effect point. Dropping
     20 /// a future after that point does not imply rollback. Replay behavior is
     21 /// advertised through signer status and every successful result must use a
     22 /// verified receipt or authored-evidence constructor.
     23 pub trait Signer: Send + Sync {
     24     fn status(&self) -> BoxFuture<'_, Result<SignerStatus, Error>>;
     25 
     26     /// Signs one already-authorized exact plan.
     27     ///
     28     /// Implementations must observe the request's millisecond deadline and
     29     /// cancellation signal throughout the operation, preserve its stable
     30     /// signer request ID for remote replay, and create success only through
     31     /// [`SignReceipt::from_signed_event`].
     32     fn sign(&self, request: SignRequest) -> BoxFuture<'_, Result<SignReceipt, Error>>;
     33 
     34     /// Signs an authored plan while retaining any verified result of started work.
     35     ///
     36     /// An override must honor deadline and cancellation before starting work,
     37     /// but may return exact evidence received afterward. Such evidence is not
     38     /// permission to resume stopped work. The composing host owns polling and
     39     /// durable reconciliation; dropping this future does not undo a signature.
     40     ///
     41     /// The default delegates to [`Self::sign`] and preserves existing adapters.
     42     /// It cannot recover evidence that the adapter discards. Blossom requests
     43     /// and already-cancelled requests are rejected before invoking that adapter.
     44     fn sign_authored_evidence(
     45         &self,
     46         request: SignRequest,
     47     ) -> BoxFuture<'_, Result<AuthoredSignEvidence, Error>> {
     48         Box::pin(async move {
     49             if request.authored_plan().is_none() {
     50                 return Err(Error::new(Kind::InvalidArgument));
     51             }
     52             if request.cancellation_signal().is_cancelled() {
     53                 return Err(Error::new(Kind::SignerCancelled));
     54             }
     55             let receipt = self.sign(request.clone()).await?;
     56             verify_identity(
     57                 receipt.operation_kind(),
     58                 receipt.intent_id(),
     59                 receipt.signer_request_id(),
     60                 &request,
     61             )?;
     62             AuthoredSignEvidence::from_signed_event(
     63                 &request,
     64                 receipt.signed_event().clone(),
     65                 receipt.completed_at_unix_ms(),
     66             )
     67         })
     68     }
     69 }
     70 
     71 /// Shared signer handle used by composing hosts without selecting a runtime.
     72 pub type DynSigner = Arc<dyn Signer>;
     73 
     74 #[cfg(test)]
     75 mod tests {
     76     use super::*;
     77 
     78     struct Stub;
     79 
     80     impl Signer for Stub {
     81         fn status(&self) -> BoxFuture<'_, Result<SignerStatus, Error>> {
     82             Box::pin(async { Ok(SignerStatus::unavailable()) })
     83         }
     84 
     85         fn sign(&self, _request: SignRequest) -> BoxFuture<'_, Result<SignReceipt, Error>> {
     86             Box::pin(async { Err(Error::new(Kind::SignerUnavailable)) })
     87         }
     88     }
     89 
     90     #[test]
     91     fn signer_remains_dyn_send_and_sync() {
     92         fn assert_dyn(_: &dyn Signer) {}
     93         fn assert_send_sync<T: Send + Sync + ?Sized>() {}
     94         assert_dyn(&Stub);
     95         assert_send_sync::<dyn Signer>();
     96     }
     97 }