#![cfg(any(test, feature = "test-support"))]
use prikk_error::Result;
use prikk_object::{
BlockKind, BlockPayload, CanonicalEncode, ObjectId, ObjectType, RefKind, RefStatePayload,
RefUpdatePayload,
};
use crate::{
ActiveLock, Ed25519MaintainerSigner, MaintainerSigner, ObjectReader, ObjectWriteSession,
ObjectWriter, RefPublication, RefStore, RepositoryLayout, Wal, derive_next_state_root,
finish_active_publication_cleanup, maintainer_signature,
};
fn signed_envelope(
object_type: ObjectType,
schema_version: u32,
canonical_payload: Vec<u8>,
signer: &Ed25519MaintainerSigner,
) -> Result<prikk_object::ObjectEnvelope> {
let mut envelope =
prikk_object::ObjectEnvelope::unsigned(object_type, schema_version, canonical_payload);
let object_id = envelope.object_id();
envelope.add_signature(maintainer_signature(signer, object_type, object_id)?)?;
Ok(envelope)
}
pub(crate) fn simulate_one_seal(
layout: &RepositoryLayout,
ref_name: &str,
signer: &Ed25519MaintainerSigner,
) -> Result<ObjectId> {
let active_lock = ActiveLock::acquire(layout)?;
let wal = Wal::for_layout(layout);
let replay = wal.replay()?;
let mut object_store = ObjectWriteSession::open(layout)?;
let ref_store = RefStore::new(layout.clone());
let current = match ref_store.read_current_ref_state_id(ref_name)? {
Some(ref_state_id) => {
let envelope = object_store
.read_typed(ref_state_id, ObjectType::RefState)?
.ok_or_else(|| {
prikk_error::PrikkError::Integrity("current RefState missing".to_string())
})?;
let payload = RefStatePayload::decode_canonical(
&envelope.canonical_payload,
envelope.schema_version,
)?;
Some((ref_state_id, payload))
}
None => None,
};
let mut patch_ids = Vec::with_capacity(replay.records.len());
for record in &replay.records {
patch_ids.push(object_store.write_object(&record.envelope)?);
}
let parent = current
.as_ref()
.map(|(_, payload)| payload.target_object_id);
let state_merkle_root = derive_next_state_root(&object_store, parent, &patch_ids)?;
let block_payload = BlockPayload {
parent_block_ids: parent.into_iter().collect(),
kind: if current.is_some() {
BlockKind::Normal
} else {
BlockKind::Root
},
patch_ids: patch_ids.clone(),
state_merkle_root,
snapshot_blob_ref: None,
mainline_parent_id: None,
merge_baseline_block_id: None,
};
let block_envelope = signed_envelope(
ObjectType::Block,
2,
block_payload.to_canonical_bytes()?,
signer,
)?;
let block_id = object_store.write_object(&block_envelope)?;
let update_seq = current
.as_ref()
.map(|(_, payload)| payload.update_seq + 1)
.unwrap_or(1);
let previous_ref_state_id = current.as_ref().map(|(id, _)| *id);
let ref_state_payload = RefStatePayload {
ref_name: ref_name.to_string(),
kind: RefKind::Branch,
target_object_id: block_id,
update_seq,
previous_ref_state_id,
required_attestation_ids: Vec::new(),
closed: false,
};
let ref_state_envelope = signed_envelope(
ObjectType::RefState,
1,
ref_state_payload.to_canonical_bytes()?,
signer,
)?;
let ref_state_id = ref_state_envelope.object_id();
let ref_update_payload = RefUpdatePayload {
ref_name: ref_name.to_string(),
old_ref_state_id: previous_ref_state_id,
new_ref_state_id: ref_state_id,
new_target_object_id: block_id,
update_seq,
created_at: 0,
author_key_id: signer.key_id().to_string(),
};
let ref_update_envelope = signed_envelope(
ObjectType::RefUpdate,
1,
ref_update_payload.to_canonical_bytes()?,
signer,
)?;
let publication = RefPublication {
ref_name: ref_name.to_string(),
expected_previous_ref_state_id: previous_ref_state_id,
ref_state: ref_state_envelope,
ref_update: ref_update_envelope,
};
let published = ref_store.finish_interrupted_publication_with_object_store(
&mut object_store,
&active_lock,
&publication,
)?;
finish_active_publication_cleanup(layout, &active_lock)?;
Ok(published)
}
#[cfg(feature = "test-support")]
pub fn simulate_one_seal_for_test_support(
layout: &RepositoryLayout,
ref_name: &str,
signer: &Ed25519MaintainerSigner,
) -> Result<ObjectId> {
simulate_one_seal(layout, ref_name, signer)
}