Skip to content

Commit 26551b9

Browse files
committed
corestore: frame transaction manifest records
1 parent 00f77fd commit 26551b9

1 file changed

Lines changed: 75 additions & 5 deletions

File tree

anvil-core/src/core_store/local.rs

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,15 @@ const CORE_STREAM_SPARSE_INDEX_MAGIC: &[u8; 8] = b"ANSSIX1\0";
7272
const CORE_ACTIVE_STREAM_MAGIC: &[u8; 8] = b"ANASTR1\0";
7373
const CORE_ROOT_ANCHOR_MAGIC: &[u8; 8] = b"ANROOT1\0";
7474
const CORE_ROOT_REGISTER_MAGIC: &[u8; 8] = b"ANREGRT1";
75+
const CORE_TRANSACTION_MANIFEST_MAGIC: &[u8; 8] = b"ANXACT1\0";
7576
const CORE_BLOCK_SHARD_MAGIC: &[u8; 8] = b"ANBLK\n\0\0";
7677
const CORE_WAL_FILE_MAGIC: &[u8; 6] = b"ANWAL\n";
7778
const CORE_WAL_FRAME_MAGIC: &[u8; 4] = b"AWF1";
7879
const CORE_STREAM_SEGMENT_VERSION: u16 = 1;
7980
const CORE_ACTIVE_STREAM_VERSION: u16 = 1;
8081
const CORE_ROOT_ANCHOR_VERSION: u16 = 1;
8182
const CORE_ROOT_REGISTER_VERSION: u16 = 1;
83+
const CORE_TRANSACTION_MANIFEST_VERSION: u16 = 1;
8284
const CORE_BLOCK_SHARD_VERSION: u16 = 1;
8385
const CORE_WAL_VERSION: u16 = 1;
8486
const CORE_WAL_EPOCH: u64 = 1;
@@ -4700,8 +4702,7 @@ impl CoreStore {
47004702
let transaction_manifest_bytes = self
47014703
.read_logical_file_plaintext(&transaction_manifest)
47024704
.await?;
4703-
let transaction: CoreTransactionManifestRecord =
4704-
serde_json::from_slice(&transaction_manifest_bytes)?;
4705+
let transaction = decode_transaction_manifest_record(&transaction_manifest_bytes)?;
47054706
validate_transaction_manifest_record(&transaction, anchor.root_generation)?;
47064707
let state_locator = transaction
47074708
.logical_manifests
@@ -4749,7 +4750,7 @@ impl CoreStore {
47494750
"core_control",
47504751
format!("lf_core_transaction_manifest_{post_root_generation:020}"),
47514752
post_root_generation,
4752-
serde_json::to_vec(&transaction)?,
4753+
encode_transaction_manifest_record(&transaction)?,
47534754
format!("core_transaction_manifest_{post_root_generation:020}"),
47544755
"local".to_string(),
47554756
)
@@ -7672,6 +7673,74 @@ fn validate_transaction_manifest_record(
76727673
Ok(())
76737674
}
76747675

7676+
fn encode_transaction_manifest_record(
7677+
transaction: &CoreTransactionManifestRecord,
7678+
) -> Result<Vec<u8>> {
7679+
let header = serde_json::json!({
7680+
"schema": &transaction.schema,
7681+
"pre_root_generation": transaction.pre_root_generation,
7682+
"post_root_generation": transaction.post_root_generation,
7683+
"mutation_count": transaction.mutation_ids.len(),
7684+
"logical_manifest_count": transaction.logical_manifests.len(),
7685+
});
7686+
let header_json = serde_json::to_vec(&header)?;
7687+
let body_json = serde_json::to_vec(transaction)?;
7688+
let mut out = Vec::with_capacity(
7689+
CORE_TRANSACTION_MANIFEST_MAGIC.len() + 2 + 4 + 8 + header_json.len() + body_json.len() + 4,
7690+
);
7691+
out.extend_from_slice(CORE_TRANSACTION_MANIFEST_MAGIC);
7692+
out.extend_from_slice(&CORE_TRANSACTION_MANIFEST_VERSION.to_le_bytes());
7693+
out.extend_from_slice(&(header_json.len() as u32).to_le_bytes());
7694+
out.extend_from_slice(&(body_json.len() as u64).to_le_bytes());
7695+
out.extend_from_slice(&header_json);
7696+
out.extend_from_slice(&body_json);
7697+
let mut crc_input = Vec::with_capacity(header_json.len() + body_json.len());
7698+
crc_input.extend_from_slice(&header_json);
7699+
crc_input.extend_from_slice(&body_json);
7700+
out.extend_from_slice(&crc32c(&crc_input).to_le_bytes());
7701+
Ok(out)
7702+
}
7703+
7704+
fn decode_transaction_manifest_record(bytes: &[u8]) -> Result<CoreTransactionManifestRecord> {
7705+
let mut offset = 0usize;
7706+
let magic = read_exact(bytes, &mut offset, CORE_TRANSACTION_MANIFEST_MAGIC.len())?;
7707+
if magic != CORE_TRANSACTION_MANIFEST_MAGIC {
7708+
bail!("CoreStore transaction manifest has invalid magic");
7709+
}
7710+
let version = read_u16_le(bytes, &mut offset)?;
7711+
if version != CORE_TRANSACTION_MANIFEST_VERSION {
7712+
bail!("CoreStore transaction manifest has unsupported version {version}");
7713+
}
7714+
let header_len = read_u32_le(bytes, &mut offset)? as usize;
7715+
let body_len = read_u64_le(bytes, &mut offset)? as usize;
7716+
let header_json = read_exact(bytes, &mut offset, header_len)?;
7717+
let body_json = read_exact(bytes, &mut offset, body_len)?;
7718+
let expected_crc = read_u32_le(bytes, &mut offset)?;
7719+
if offset != bytes.len() {
7720+
bail!("CoreStore transaction manifest has trailing bytes");
7721+
}
7722+
let mut crc_input = Vec::with_capacity(header_json.len() + body_json.len());
7723+
crc_input.extend_from_slice(header_json);
7724+
crc_input.extend_from_slice(body_json);
7725+
if crc32c(&crc_input) != expected_crc {
7726+
bail!("CoreStore transaction manifest checksum mismatch");
7727+
}
7728+
let header: serde_json::Value = serde_json::from_slice(header_json)?;
7729+
if json_required_string(&header, "schema")? != "anvil.core.transaction_manifest.v1" {
7730+
bail!("CoreStore transaction manifest header has invalid schema");
7731+
}
7732+
let transaction: CoreTransactionManifestRecord = serde_json::from_slice(body_json)?;
7733+
if json_required_u64(&header, "pre_root_generation")? != transaction.pre_root_generation
7734+
|| json_required_u64(&header, "post_root_generation")? != transaction.post_root_generation
7735+
|| json_required_u64(&header, "mutation_count")? != transaction.mutation_ids.len() as u64
7736+
|| json_required_u64(&header, "logical_manifest_count")?
7737+
!= transaction.logical_manifests.len() as u64
7738+
{
7739+
bail!("CoreStore transaction manifest header/body mismatch");
7740+
}
7741+
Ok(transaction)
7742+
}
7743+
76757744
fn hash_root_anchor_record(anchor: &CoreRootAnchorRecord) -> Result<String> {
76767745
Ok(format!(
76777746
"sha256:{}",
@@ -11050,8 +11119,9 @@ mod tests {
1105011119
.read_logical_file_plaintext(&transaction_manifest)
1105111120
.await
1105211121
.unwrap();
11053-
let transaction_manifest: CoreTransactionManifestRecord =
11054-
serde_json::from_slice(&transaction_manifest_bytes).unwrap();
11122+
assert!(transaction_manifest_bytes.starts_with(CORE_TRANSACTION_MANIFEST_MAGIC));
11123+
let transaction_manifest = decode_transaction_manifest_record(&transaction_manifest_bytes)
11124+
.expect("decode transaction manifest frame");
1105511125
assert_eq!(
1105611126
transaction_manifest.post_root_generation,
1105711127
latest_anchor.root_generation

0 commit comments

Comments
 (0)