use prikk_error::Result;
use crate::index::{replay_index_decode_count_for_test, reset_replay_index_decode_count_for_test};
use crate::rfc111_seal_simulation::simulate_one_seal;
use crate::test_support::unique_temp_dir;
use crate::worktree_patch::{WorktreePatchCommitOptions, commit_worktree_changes_signed};
use crate::{
Ed25519AuthorSigner, Ed25519MaintainerSigner, MaintainerSigner, RepositoryLayout,
add_trusted_maintainer,
};
const REF_NAME: &str = "heads/main";
fn maintainer_signer() -> Result<Ed25519MaintainerSigner> {
Ed25519MaintainerSigner::from_seed("rfc111-seal-gate-maintainer", &[0x72; 32])
}
fn author_signer() -> Result<Ed25519AuthorSigner> {
Ed25519AuthorSigner::from_seed("rfc111-seal-gate-author", &[0x71; 32])
}
fn commit_and_seal(
layout: &RepositoryLayout,
index: usize,
maintainer: &Ed25519MaintainerSigner,
) -> Result<()> {
let signer = author_signer()?;
let path = format!("f{index}.txt");
std::fs::write(layout.root().join(&path), format!("{path}\n").into_bytes())?;
commit_worktree_changes_signed(
layout,
REF_NAME,
"rfc111-seal-gate",
WorktreePatchCommitOptions::default(),
&signer,
)?;
simulate_one_seal(layout, REF_NAME, maintainer)?;
Ok(())
}
fn measure_replay_index_decodes_for_one_more_seal(sealed_block_count: usize) -> Result<usize> {
let root = unique_temp_dir(&format!(
"rfc111-seal-cost-gate-{sealed_block_count}-blocks"
));
let layout = RepositoryLayout::init(root.clone())?;
let maintainer = maintainer_signer()?;
add_trusted_maintainer(
&layout,
maintainer.key_id(),
&prikk_hash::to_hex(&maintainer.public_key_bytes()),
)?;
for index in 0..sealed_block_count {
commit_and_seal(&layout, index, &maintainer)?;
}
reset_replay_index_decode_count_for_test();
commit_and_seal(&layout, sealed_block_count, &maintainer)?;
let decode_count = replay_index_decode_count_for_test();
let _ = std::fs::remove_dir_all(root);
Ok(decode_count)
}
#[test]
fn seal_index_decode_count_does_not_grow_with_prior_history() -> Result<()> {
const SMALL: usize = 4;
const LARGE: usize = 16;
let small_decodes = measure_replay_index_decodes_for_one_more_seal(SMALL)?;
let large_decodes = measure_replay_index_decodes_for_one_more_seal(LARGE)?;
assert_eq!(
small_decodes,
large_decodes,
"sealing one more commit's full-index-decode count must not grow with how much history \
already exists (RFC 111 Stage 2 handoff §1), but sealing the {}th block after a \
{SMALL}-block history triggered {small_decodes} decode(s) while sealing the {}th block \
after a {LARGE}-block history triggered {large_decodes} -- proportional to prior history, \
exactly the O(N)-per-seal-read regression RFC 111 exists to close (`derive_next_state_root`'s \
ancestor walk, each read costing a full index decode)",
SMALL + 1,
LARGE + 1
);
Ok(())
}