use prost::Message;
use crate::heddle::api::v1alpha2::{SpoolCreationProof, ThreadControlAuthority};
const OWNER_MINT_ROOT_ATTACHMENT_FIELD: u64 = 4;
const PASSKEY_MINT_ROOT_ATTACHMENT_FIELD: u64 = 6;
const MAX_PROTOBUF_FIELD_NUMBER: u64 = (1 << 29) - 1;
#[derive(Debug, thiserror::Error)]
pub enum MintRootAssociationWireError {
#[error("mint-root association contains both owner-v1 and passkey-v2 fields")]
BothArms,
#[error("mint-root association contains malformed protobuf wire bytes")]
Malformed,
#[error("mint-root association protobuf decode failed: {0}")]
Decode(#[from] prost::DecodeError),
}
fn read_varint(bytes: &[u8], offset: &mut usize) -> Result<u64, MintRootAssociationWireError> {
let mut value = 0_u64;
for index in 0..10 {
let byte = *bytes
.get(*offset)
.ok_or(MintRootAssociationWireError::Malformed)?;
*offset += 1;
if index == 9 && byte > 1 {
return Err(MintRootAssociationWireError::Malformed);
}
value |= u64::from(byte & 0x7f) << (index * 7);
if byte & 0x80 == 0 {
return Ok(value);
}
}
Err(MintRootAssociationWireError::Malformed)
}
fn skip(
bytes: &[u8],
offset: &mut usize,
length: usize,
) -> Result<(), MintRootAssociationWireError> {
*offset = offset
.checked_add(length)
.filter(|end| *end <= bytes.len())
.ok_or(MintRootAssociationWireError::Malformed)?;
Ok(())
}
pub fn verify_mint_root_association_wire(bytes: &[u8]) -> Result<(), MintRootAssociationWireError> {
let mut offset = 0;
let mut owner = false;
let mut passkey = false;
while offset < bytes.len() {
let key = read_varint(bytes, &mut offset)?;
let field = key >> 3;
let wire_type = key & 0x07;
if field == 0 || field > MAX_PROTOBUF_FIELD_NUMBER {
return Err(MintRootAssociationWireError::Malformed);
}
match field {
OWNER_MINT_ROOT_ATTACHMENT_FIELD => owner = true,
PASSKEY_MINT_ROOT_ATTACHMENT_FIELD => passkey = true,
_ => {}
}
if owner && passkey {
return Err(MintRootAssociationWireError::BothArms);
}
match wire_type {
0 => {
read_varint(bytes, &mut offset)?;
}
1 => skip(bytes, &mut offset, 8)?,
2 => {
let length = usize::try_from(read_varint(bytes, &mut offset)?)
.map_err(|_| MintRootAssociationWireError::Malformed)?;
skip(bytes, &mut offset, length)?;
}
5 => skip(bytes, &mut offset, 4)?,
_ => return Err(MintRootAssociationWireError::Malformed),
}
}
Ok(())
}
pub fn decode_thread_control_authority_for_verification(
bytes: &[u8],
) -> Result<ThreadControlAuthority, MintRootAssociationWireError> {
verify_mint_root_association_wire(bytes)?;
Ok(ThreadControlAuthority::decode(bytes)?)
}
pub fn decode_spool_creation_proof_for_verification(
bytes: &[u8],
) -> Result<SpoolCreationProof, MintRootAssociationWireError> {
verify_mint_root_association_wire(bytes)?;
Ok(SpoolCreationProof::decode(bytes)?)
}