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);
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, [8; 32]), 9);
let sections = owner.encode_checkpoint_identities()?;
let mut tokens = CheckpointBodyDecoder::new(§ions.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(§ions.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, [8; 32])),
Some(&9)
);
assert_eq!(owner.encode_checkpoint_debt()?, vec![0]);
Ok(())
}
}