use crate::model::ic_update_response::IcUpdateResponseInput;
use crate::model::{
artifacts::ArtifactChecksumRecord,
attempt_journal::{AttemptAuthorityRecord, AttemptJournalRecord},
ic_snapshot_data::{IcSnapshotDataRequest, MAX_IC_SNAPSHOT_DATA_REPLY_BYTES},
ic_snapshot_metadata::IcSnapshotMetadataRequest,
operation_plan::{OperationPlanError, OperationPlanRecord},
};
use std::fmt;
use thiserror::Error;
#[derive(Clone, Copy, Debug)]
pub enum IcSnapshotTransferReadPayload<'request, 'metadata> {
Metadata(&'request IcSnapshotMetadataRequest),
Data(&'request IcSnapshotDataRequest<'metadata>),
}
impl IcSnapshotTransferReadPayload<'_, '_> {
pub(crate) fn validate_binding(
&self,
authority: &AttemptAuthorityRecord,
) -> Result<(), IcSnapshotTransferReadError> {
if self.target() != authority.binding().target()
|| self.digest().hash() != authority.binding().request()
{
return Err(IcSnapshotTransferReadError::PayloadMismatch);
}
Ok(())
}
#[must_use]
pub fn target(&self) -> &str {
match self {
Self::Metadata(payload) => payload.target(),
Self::Data(payload) => payload.target(),
}
}
#[must_use]
pub const fn receiver(&self) -> &'static str {
match self {
Self::Metadata(payload) => payload.receiver(),
Self::Data(payload) => payload.receiver(),
}
}
#[must_use]
pub const fn method(&self) -> &'static str {
match self {
Self::Metadata(payload) => payload.method(),
Self::Data(payload) => payload.method(),
}
}
#[must_use]
pub fn arguments(&self) -> &[u8] {
match self {
Self::Metadata(payload) => payload.arguments(),
Self::Data(payload) => payload.arguments(),
}
}
#[must_use]
pub fn digest(&self) -> ArtifactChecksumRecord {
match self {
Self::Metadata(payload) => payload.digest(),
Self::Data(payload) => payload.digest(),
}
}
}
#[derive(Debug)]
pub struct IcSnapshotTransferReadRequest<'request, 'metadata> {
plan: &'request OperationPlanRecord,
payload: IcSnapshotTransferReadPayload<'request, 'metadata>,
authority: AttemptAuthorityRecord,
mutation_attempt: u32,
}
impl<'request, 'metadata> IcSnapshotTransferReadRequest<'request, 'metadata> {
pub fn new(
plan: &'request OperationPlanRecord,
operation_sequence: u64,
journal: &AttemptJournalRecord,
payload: IcSnapshotTransferReadPayload<'request, 'metadata>,
) -> Result<Self, IcSnapshotTransferReadError> {
let authority = plan.attempt_authority(operation_sequence)?;
if journal.authority() != &authority {
return Err(IcSnapshotTransferReadError::AuthorityMismatch);
}
payload.validate_binding(&authority)?;
let mutation_attempt = journal
.view()
.pending_mutation
.ok_or(IcSnapshotTransferReadError::NoPendingMutation)?;
let request = Self {
plan,
payload,
authority,
mutation_attempt,
};
request.validate_journal(journal)?;
Ok(request)
}
#[must_use]
pub const fn plan(&self) -> &'request OperationPlanRecord {
self.plan
}
#[must_use]
pub const fn payload(&self) -> IcSnapshotTransferReadPayload<'request, 'metadata> {
self.payload
}
#[must_use]
pub const fn authority(&self) -> &AttemptAuthorityRecord {
&self.authority
}
#[must_use]
pub const fn mutation_attempt(&self) -> u32 {
self.mutation_attempt
}
pub fn validate_journal(
&self,
journal: &AttemptJournalRecord,
) -> Result<(), IcSnapshotTransferReadError> {
if journal.authority() != &self.authority {
return Err(IcSnapshotTransferReadError::AuthorityMismatch);
}
let view = journal.view();
if view.pending_mutation != Some(self.mutation_attempt) {
return Err(IcSnapshotTransferReadError::MutationMismatch);
}
if view.pending_observation.is_some() {
return Err(IcSnapshotTransferReadError::ObservationPending);
}
Ok(())
}
}
#[derive(Clone)]
pub struct IcSnapshotTransferReadResponse {
input: IcUpdateResponseInput,
}
impl IcSnapshotTransferReadResponse {
pub fn new(mut input: IcUpdateResponseInput) -> Result<Self, IcSnapshotTransferReadError> {
input.admit(
MAX_IC_SNAPSHOT_DATA_REPLY_BYTES,
IcSnapshotTransferReadError::InvalidAttempt,
IcSnapshotTransferReadError::ReplyTooLarge,
IcSnapshotTransferReadError::InvalidTarget,
)?;
Ok(Self { input })
}
#[must_use]
pub const fn input(&self) -> &IcUpdateResponseInput {
&self.input
}
}
impl fmt::Debug for IcSnapshotTransferReadResponse {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.input
.fmt_redacted(formatter, "IcSnapshotTransferReadResponse")
}
}
#[derive(Debug, Error)]
pub enum IcSnapshotTransferReadError {
#[error("snapshot transfer read authority differs")]
AuthorityMismatch,
#[error("snapshot transfer read payload differs")]
PayloadMismatch,
#[error("snapshot transfer read requires a pending original update")]
NoPendingMutation,
#[error("snapshot transfer read original attempt differs")]
MutationMismatch,
#[error("snapshot transfer read recovery observation is pending")]
ObservationPending,
#[error("invalid snapshot transfer read attempt")]
InvalidAttempt,
#[error("snapshot transfer read reply too large")]
ReplyTooLarge,
#[error("invalid snapshot transfer read target")]
InvalidTarget,
#[error(transparent)]
Plan(#[from] OperationPlanError),
}