liminal-server 0.24.9

Standalone server for the liminal messaging bus
Documentation
use super::super::restore_checkpoint::CheckpointError;
use super::ConversationAuthority;
use super::checkpoint_body::{CheckpointBodyEncoder, invalid};

pub(in crate::server::participant::production) struct CheckpointIdentitySections {
    pub tokens: Vec<u8>,
    pub committed_admissions: Vec<u8>,
    pub retired: Vec<u8>,
}

impl ConversationAuthority {
    pub(in crate::server::participant::production) fn encode_checkpoint_identities(
        &self,
    ) -> Result<CheckpointIdentitySections, CheckpointError> {
        Ok(CheckpointIdentitySections {
            tokens: self.encode_checkpoint_tokens()?,
            committed_admissions: self.encode_checkpoint_admissions()?,
            retired: self.encode_checkpoint_retired()?,
        })
    }

    fn encode_checkpoint_tokens(&self) -> Result<Vec<u8>, CheckpointError> {
        let mut encoder = CheckpointBodyEncoder::new();
        encoder.count("tokens", self.tokens.len())?;
        for (token, participant) in &self.tokens {
            encoder.raw(token);
            encoder.bytes("token participant", &participant.to_le_bytes())?;
        }
        Ok(encoder.finish())
    }

    fn encode_checkpoint_admissions(&self) -> Result<Vec<u8>, CheckpointError> {
        let mut encoder = CheckpointBodyEncoder::new();
        encoder.count("committed admissions", self.committed_admissions.len())?;
        for ((token, participant, fingerprint), delivery) in &self.committed_admissions {
            encoder.raw(token);
            encoder.u64(*participant);
            encoder.raw(fingerprint.as_bytes());
            encoder.bytes("admission delivery", &delivery.to_le_bytes())?;
        }
        Ok(encoder.finish())
    }

    fn encode_checkpoint_retired(&self) -> Result<Vec<u8>, CheckpointError> {
        let mut encoder = CheckpointBodyEncoder::new();
        encoder.count("retired", self.retired.len())?;
        for (participant, retired) in &self.retired {
            if retired.participant_id() != *participant
                || retired.conversation_id() != self.conversation_id
            {
                return Err(invalid("retired identity association"));
            }
            encoder.u64(*participant);
            encoder.bytes(
                "retired identity",
                &retired.storage_description().encode_canonical(),
            )?;
        }
        Ok(encoder.finish())
    }
}

#[derive(Debug, thiserror::Error)]
enum DebtEncodingError {
    #[error("debt cursor encoding refused: {0:?}")]
    CursorFacts(liminal_protocol::lifecycle::CursorFactEncodeError),
}

impl ConversationAuthority {
    pub(in crate::server::participant::production) fn encode_checkpoint_debt(
        &self,
    ) -> Result<Vec<u8>, CheckpointError> {
        let mut encoder = CheckpointBodyEncoder::new();
        match self.checkpoint_debt_owner() {
            None => encoder.u8(0),
            Some(state) => {
                let mut body = CheckpointBodyEncoder::new();
                match state.episode() {
                    None => body.u8(0),
                    Some(episode) => {
                        body.u8(1);
                        let bytes =
                            episode
                                .encode()
                                .map_err(|error| CheckpointError::LiveState {
                                    source: Box::new(DebtEncodingError::CursorFacts(error)),
                                })?;
                        body.bytes("debt episode", &bytes)?;
                    }
                }
                body.bytes(
                    "closure accounting",
                    &state.frontier().encode_closure_accounting_source(),
                )?;
                let charges = state
                    .frontier()
                    .encode_retained_charge_source()
                    .ok_or_else(|| super::checkpoint_body::invalid("retained charge count"))?;
                body.bytes("retained charges", &charges)?;
                encoder.u8(1);
                encoder.bytes("debt dispatch", &body.finish())?;
            }
        }
        Ok(encoder.finish())
    }
}

#[cfg(test)]
mod tests {
    use super::super::checkpoint_body::CheckpointBodyDecoder;
    use super::*;
    use crate::server::participant::production::barrier::ReceiptCapacityLimits;

    #[test]
    fn borrowed_identity_sections_preserve_exact_keys_and_leave_owner_held()
    -> Result<(), CheckpointError> {
        let config = crate::server::participant::test_participant_config();
        let mut owner =
            ConversationAuthority::empty(7, ReceiptCapacityLimits::from_config(&config));
        owner.tokens.insert([2; 16], 4);
        owner.tokens.insert([1; 16], 3);
        owner.committed_admissions.insert(
            (
                [6; 16],
                4,
                liminal_protocol::wire::CredentialDigest::new([8; 32]),
            ),
            9,
        );
        let sections = owner.encode_checkpoint_identities()?;
        let mut tokens = CheckpointBodyDecoder::new(&sections.tokens);
        assert_eq!(tokens.count("tokens", 28)?, 2);
        for (token, participant) in [([1; 16], 3_u64), ([2; 16], 4_u64)] {
            assert_eq!(tokens.take("token", 16)?, &token);
            assert_eq!(tokens.bytes("participant")?, participant.to_le_bytes());
        }
        tokens.finish()?;
        let mut admissions = CheckpointBodyDecoder::new(&sections.committed_admissions);
        assert_eq!(admissions.count("admissions", 68)?, 1);
        assert_eq!(admissions.take("token", 16)?, &[6; 16]);
        assert_eq!(admissions.u64("participant")?, 4);
        assert_eq!(admissions.take("fingerprint", 32)?, &[8; 32]);
        assert_eq!(admissions.bytes("delivery")?, 9_u64.to_le_bytes());
        admissions.finish()?;
        assert_eq!(sections.retired, vec![0; 4]);
        assert_eq!(owner.tokens.len(), 2);
        assert_eq!(owner.tokens.get(&[1; 16]), Some(&3));
        assert_eq!(
            owner.committed_admissions.get(&(
                [6; 16],
                4,
                liminal_protocol::wire::CredentialDigest::new([8; 32])
            )),
            Some(&9)
        );
        assert_eq!(owner.encode_checkpoint_debt()?, vec![0]);
        Ok(())
    }
}