use crate::errors::FirewallError;
use crate::types::{GovernanceHeader, RegistryEntry, RegistryPayload};
pub(crate) fn parse_governance_header(
data: &[u8],
offset: usize,
gov_len: usize,
) -> Result<GovernanceHeader, FirewallError> {
if data.len() < offset + gov_len || gov_len < 3 {
return Err(FirewallError::InvalidRegistryData);
}
let gh_version = data[offset];
if gh_version != 0x01 && gh_version != 0x02 && gh_version != 0x03 {
return Err(FirewallError::InvalidRegistryData);
}
let signer_count = data[offset + 1] as usize;
let threshold = data[offset + 2];
let pubkeys_end = offset + 3 + signer_count * 33;
if pubkeys_end + 2 + 32 > offset + gov_len {
return Err(FirewallError::InvalidRegistryData);
}
let mut pubkeys = Vec::with_capacity(signer_count);
for i in 0..signer_count {
let start = offset + 3 + i * 33;
let mut pk = [0u8; 33];
pk.copy_from_slice(&data[start..start + 33]);
pubkeys.push(pk);
}
let validator_count = u16::from_le_bytes([data[pubkeys_end], data[pubkeys_end + 1]]);
let mut validator_merkle_root = [0u8; 32];
validator_merkle_root.copy_from_slice(&data[pubkeys_end + 2..pubkeys_end + 34]);
Ok(GovernanceHeader {
signer_count: signer_count as u8,
threshold,
pubkeys,
validator_count,
validator_merkle_root,
})
}
pub(crate) fn parse_entries(
data: &[u8],
offset: usize,
count: usize,
) -> Result<(Vec<RegistryEntry>, usize), FirewallError> {
let mut off = offset;
let max_possible = data.len().saturating_sub(off) / 9;
if count > max_possible {
return Err(FirewallError::InvalidRegistryData);
}
let mut entries = Vec::with_capacity(count);
for _ in 0..count {
if off >= data.len() {
return Err(FirewallError::InvalidRegistryData);
}
let id_len = data[off] as usize;
off += 1;
if off + id_len + 8 > data.len() {
return Err(FirewallError::InvalidRegistryData);
}
let identifier = data[off..off + id_len].to_vec();
off += id_len;
let expires_at = u64::from_le_bytes([
data[off],
data[off + 1],
data[off + 2],
data[off + 3],
data[off + 4],
data[off + 5],
data[off + 6],
data[off + 7],
]);
off += 8;
entries.push(RegistryEntry {
identifier,
expires_at,
});
}
for i in 1..entries.len() {
if entries[i].identifier <= entries[i - 1].identifier {
return Err(FirewallError::RegistryNotSorted);
}
}
Ok((entries, off))
}
pub fn parse_registry_payload(data: &[u8]) -> Result<RegistryPayload, FirewallError> {
if data.len() < 7 {
return Err(FirewallError::InvalidRegistryData);
}
if &data[0..4] != b"BLKL" {
return Err(FirewallError::InvalidRegistryData);
}
if data[4] != 0x02 {
return Err(FirewallError::InvalidRegistryData);
}
let gov_len = u16::from_le_bytes([data[5], data[6]]) as usize;
if data.len() < 7 + gov_len + 4 {
return Err(FirewallError::InvalidRegistryData);
}
let governance_header = parse_governance_header(data, 7, gov_len)?;
let entries_start = 7 + gov_len;
let count = u32::from_le_bytes([
data[entries_start],
data[entries_start + 1],
data[entries_start + 2],
data[entries_start + 3],
]) as usize;
let (entries, end) = parse_entries(data, entries_start + 4, count)?;
if end != data.len() {
return Err(FirewallError::InvalidRegistryData);
}
Ok(RegistryPayload {
version: 2,
entries,
governance_header: Some(governance_header),
})
}
pub fn encode_governance_header(gh: &GovernanceHeader) -> Vec<u8> {
let mut out = Vec::new();
out.push(0x01);
out.push(gh.signer_count);
out.push(gh.threshold);
for pk in &gh.pubkeys {
out.extend_from_slice(pk);
}
out.extend_from_slice(&gh.validator_count.to_le_bytes());
out.extend_from_slice(&gh.validator_merkle_root);
out
}
pub fn encode_registry_payload(payload: &RegistryPayload) -> Result<Vec<u8>, FirewallError> {
let gov_bytes = match &payload.governance_header {
Some(gh) => encode_governance_header(gh),
None => {
let mut h = vec![0x01u8, 0x00, 0x00, 0x00, 0x00];
h.extend_from_slice(&[0u8; 32]);
h
}
};
let gov_len = gov_bytes.len() as u16;
let mut out = Vec::new();
out.extend_from_slice(b"BLKL");
out.push(0x02);
out.extend_from_slice(&gov_len.to_le_bytes());
out.extend_from_slice(&gov_bytes);
out.extend_from_slice(&(payload.entries.len() as u32).to_le_bytes());
for entry in &payload.entries {
if entry.identifier.len() > u8::MAX as usize {
return Err(FirewallError::InvalidRegistryData);
}
out.push(entry.identifier.len() as u8);
out.extend_from_slice(&entry.identifier);
out.extend_from_slice(&entry.expires_at.to_le_bytes());
}
Ok(out)
}