chio-kernel 0.1.2

Chio runtime kernel: capability validation, guard evaluation, receipt signing
Documentation
use super::*;
use crate::tool_outcome::sign_channel_terminal_outcome_commitment;
use crate::Keypair;
use chio_settle::channel::{
    SignedChannelTerminalOutcomeCommitmentV1, VerifiedAdmittedChannelReservationV1,
};

#[derive(Debug, thiserror::Error)]
pub enum ChannelTerminalAuthorityError {
    #[error("channel terminal authority is unavailable: {0}")]
    Unavailable(String),
    #[error("channel terminal reservation was not found")]
    NotFound,
    #[error("channel terminal authority was fenced")]
    Fenced,
    #[error("channel terminal authority conflict: {0}")]
    Conflict(String),
    #[error("channel terminal authority outcome is unknown: {0}")]
    OutcomeUnknown(String),
    #[error("channel terminal authority returned mismatched evidence")]
    BindingMismatch,
}

pub struct ChannelTerminalAdvanceRequest<'a> {
    operation: &'a AdmissionOperationV1,
    context: &'a AdmissionProjectionContext,
    reservation: &'a VerifiedAdmittedChannelReservationV1,
    receipt: &'a VerifiedAdmissionReceipt,
    terminal_outcome: &'a SignedChannelTerminalOutcomeCommitmentV1,
}

impl<'a> ChannelTerminalAdvanceRequest<'a> {
    #[must_use]
    pub const fn operation(&self) -> &'a AdmissionOperationV1 {
        self.operation
    }

    #[must_use]
    pub const fn context(&self) -> &'a AdmissionProjectionContext {
        self.context
    }

    #[must_use]
    pub const fn reservation(&self) -> &'a VerifiedAdmittedChannelReservationV1 {
        self.reservation
    }

    #[must_use]
    pub const fn receipt(&self) -> &'a VerifiedAdmissionReceipt {
        self.receipt
    }

    #[must_use]
    pub const fn terminal_outcome(&self) -> &'a SignedChannelTerminalOutcomeCommitmentV1 {
        self.terminal_outcome
    }
}

#[derive(Debug, Clone)]
pub struct PreparedChannelTerminalProjectionV1 {
    reservation: VerifiedAdmittedChannelReservationV1,
    terminal_outcome: SignedChannelTerminalOutcomeCommitmentV1,
    advance: VerifiedChannelTerminalAdvanceV1,
    channel: VerifiedChannelTerminalProjectionV1,
    obligation: Option<ObligationProjection>,
}

impl PreparedChannelTerminalProjectionV1 {
    #[must_use]
    pub const fn reservation(&self) -> &VerifiedAdmittedChannelReservationV1 {
        &self.reservation
    }

    #[must_use]
    pub const fn terminal_outcome(&self) -> &SignedChannelTerminalOutcomeCommitmentV1 {
        &self.terminal_outcome
    }

    #[must_use]
    pub const fn advance(&self) -> &VerifiedChannelTerminalAdvanceV1 {
        &self.advance
    }

    #[must_use]
    pub const fn channel(&self) -> &VerifiedChannelTerminalProjectionV1 {
        &self.channel
    }

    #[must_use]
    pub const fn obligation(&self) -> Option<&ObligationProjection> {
        self.obligation.as_ref()
    }
}

pub struct ChannelTerminalCommitRequest<'a> {
    prepared: &'a PreparedChannelTerminalProjectionV1,
    recovery_lease: &'a AdmissionRecoveryLease,
    envelope: &'a SignedAdmissionTerminalProjectionV1,
    active_fence: &'a StoreMutationFence,
    trusted_now_unix_ms: u64,
}

impl<'a> ChannelTerminalCommitRequest<'a> {
    #[must_use]
    pub const fn prepared(&self) -> &'a PreparedChannelTerminalProjectionV1 {
        self.prepared
    }

    #[must_use]
    pub const fn recovery_lease(&self) -> &'a AdmissionRecoveryLease {
        self.recovery_lease
    }

    #[must_use]
    pub const fn envelope(&self) -> &'a SignedAdmissionTerminalProjectionV1 {
        self.envelope
    }

    #[must_use]
    pub const fn active_fence(&self) -> &'a StoreMutationFence {
        self.active_fence
    }

    #[must_use]
    pub const fn trusted_now_unix_ms(&self) -> u64 {
        self.trusted_now_unix_ms
    }
}

pub trait QualifiedChannelTerminalAuthority: Send + Sync {
    fn load_admitted_reservation(
        &self,
        operation: &AdmissionOperationV1,
        context: &AdmissionProjectionContext,
    ) -> Result<VerifiedAdmittedChannelReservationV1, ChannelTerminalAuthorityError>;

    fn prepare_terminal_advance(
        &self,
        request: ChannelTerminalAdvanceRequest<'_>,
    ) -> Result<VerifiedChannelTerminalAdvanceV1, ChannelTerminalAuthorityError>;

    fn commit_anchored_terminal_projection(
        &self,
        request: ChannelTerminalCommitRequest<'_>,
    ) -> Result<AdmissionTerminal, ChannelTerminalAuthorityError>;
}

fn same_admitted_reservation(
    expected: &VerifiedAdmittedChannelReservationV1,
    actual: &VerifiedAdmittedChannelReservationV1,
) -> bool {
    let expected_snapshot = expected.snapshot();
    let actual_snapshot = actual.snapshot();
    expected.proposal() == actual.proposal()
        && expected_snapshot.lifecycle() == actual_snapshot.lifecycle()
        && expected_snapshot.escrow() == actual_snapshot.escrow()
        && expected_snapshot.settlement_authority_scope_id()
            == actual_snapshot.settlement_authority_scope_id()
        && expected_snapshot.checkpoint_sequence() == actual_snapshot.checkpoint_sequence()
        && expected_snapshot.checkpoint_digest() == actual_snapshot.checkpoint_digest()
        && expected_snapshot.channel_head_digest() == actual_snapshot.channel_head_digest()
        && expected_snapshot.escrow_head_digest() == actual_snapshot.escrow_head_digest()
        && expected_snapshot.channel_predecessor_digest()
            == actual_snapshot.channel_predecessor_digest()
        && expected_snapshot.escrow_predecessor_digest()
            == actual_snapshot.escrow_predecessor_digest()
        && expected_snapshot.observed_at_unix_ms() == actual_snapshot.observed_at_unix_ms()
        && expected.ready_effect() == actual.ready_effect()
        && expected.ready_effect_head_digest() == actual.ready_effect_head_digest()
}

pub(crate) fn prepare_channel_terminal_projection(
    authority: Option<&dyn QualifiedChannelTerminalAuthority>,
    operation: &AdmissionOperationV1,
    context: &AdmissionProjectionContext,
    receipt: &VerifiedAdmissionReceipt,
    tool_outcome: &ToolOutcomeTerminalEvidenceV1,
    kernel_keypair: &Keypair,
) -> Result<PreparedChannelTerminalProjectionV1, ChannelTerminalAuthorityError> {
    if !operation.binding().participant_requirements().channel {
        return Err(ChannelTerminalAuthorityError::BindingMismatch);
    }
    let authority = authority.ok_or_else(|| {
        ChannelTerminalAuthorityError::Unavailable(
            "no qualified channel terminal authority is configured".to_owned(),
        )
    })?;
    let reservation = authority.load_admitted_reservation(operation, context)?;
    let terminal_outcome = sign_channel_terminal_outcome_commitment(
        operation,
        &reservation,
        receipt,
        tool_outcome,
        context,
        kernel_keypair,
    )
    .map_err(|_| ChannelTerminalAuthorityError::BindingMismatch)?;
    let advance = authority.prepare_terminal_advance(ChannelTerminalAdvanceRequest {
        operation,
        context,
        reservation: &reservation,
        receipt,
        terminal_outcome: &terminal_outcome,
    })?;
    let result = &terminal_outcome.body.terminal_result;
    if !same_admitted_reservation(&reservation, advance.reservation())
        || advance.effect_result_id() != result.result_id
        || advance.effect_result_digest() != result.result_digest
        || advance.effect_result() != &result.result
    {
        return Err(ChannelTerminalAuthorityError::BindingMismatch);
    }
    let (channel, obligation) = VerifiedChannelTerminalProjectionV1::from_verified(
        operation,
        context,
        receipt,
        tool_outcome,
        &advance,
    )
    .map_err(|_| ChannelTerminalAuthorityError::BindingMismatch)?;
    Ok(PreparedChannelTerminalProjectionV1 {
        reservation,
        terminal_outcome,
        advance,
        channel,
        obligation,
    })
}

#[allow(clippy::too_many_arguments)]
pub(crate) fn commit_prepared_channel_terminal_projection(
    authority: &dyn QualifiedChannelTerminalAuthority,
    operation: &AdmissionOperationV1,
    recovery_lease: &AdmissionRecoveryLease,
    projection: &AdmissionTerminalProjection,
    capabilities: &AdmissionProjectionCapabilities,
    prepared: &PreparedChannelTerminalProjectionV1,
    kernel_keypair: &Keypair,
    active_fence: &StoreMutationFence,
    trusted_now_unix_ms: u64,
) -> Result<AdmissionTerminal, ChannelTerminalAuthorityError> {
    let envelope = SignedAdmissionTerminalProjectionV1::from_verified(
        operation,
        projection,
        capabilities,
        kernel_keypair,
    )
    .map_err(|_| ChannelTerminalAuthorityError::BindingMismatch)?;
    let verified = envelope
        .verify()
        .map_err(|_| ChannelTerminalAuthorityError::BindingMismatch)?;
    if verified.channel_terminal() != Some(prepared.channel()) {
        return Err(ChannelTerminalAuthorityError::BindingMismatch);
    }
    let terminal = authority.commit_anchored_terminal_projection(ChannelTerminalCommitRequest {
        prepared,
        recovery_lease,
        envelope: &envelope,
        active_fence,
        trusted_now_unix_ms,
    })?;
    let expected = verified.terminal_operation();
    if terminal.operation_id != *expected.binding().operation_id()
        || terminal.state != expected.state()
        || Some(&terminal.replay) != expected.terminal_replay()
    {
        return Err(ChannelTerminalAuthorityError::BindingMismatch);
    }
    Ok(terminal)
}