heddle_api/
mint_root_association.rs1use prost::Message;
5
6use crate::heddle::api::v1alpha2::{SpoolCreationProof, ThreadControlAuthority};
7
8const OWNER_MINT_ROOT_ATTACHMENT_FIELD: u64 = 4;
9const PASSKEY_MINT_ROOT_ATTACHMENT_FIELD: u64 = 6;
10const MAX_PROTOBUF_FIELD_NUMBER: u64 = (1 << 29) - 1;
11
12#[derive(Debug, thiserror::Error)]
14pub enum MintRootAssociationWireError {
15 #[error("mint-root association contains both owner-v1 and passkey-v2 fields")]
17 BothArms,
18 #[error("mint-root association contains malformed protobuf wire bytes")]
20 Malformed,
21 #[error("mint-root association protobuf decode failed: {0}")]
23 Decode(#[from] prost::DecodeError),
24}
25
26fn read_varint(bytes: &[u8], offset: &mut usize) -> Result<u64, MintRootAssociationWireError> {
27 let mut value = 0_u64;
28 for index in 0..10 {
29 let byte = *bytes
30 .get(*offset)
31 .ok_or(MintRootAssociationWireError::Malformed)?;
32 *offset += 1;
33 if index == 9 && byte > 1 {
34 return Err(MintRootAssociationWireError::Malformed);
35 }
36 value |= u64::from(byte & 0x7f) << (index * 7);
37 if byte & 0x80 == 0 {
38 return Ok(value);
39 }
40 }
41 Err(MintRootAssociationWireError::Malformed)
42}
43
44fn skip(
45 bytes: &[u8],
46 offset: &mut usize,
47 length: usize,
48) -> Result<(), MintRootAssociationWireError> {
49 *offset = offset
50 .checked_add(length)
51 .filter(|end| *end <= bytes.len())
52 .ok_or(MintRootAssociationWireError::Malformed)?;
53 Ok(())
54}
55
56pub fn verify_mint_root_association_wire(bytes: &[u8]) -> Result<(), MintRootAssociationWireError> {
59 let mut offset = 0;
60 let mut owner = false;
61 let mut passkey = false;
62 while offset < bytes.len() {
63 let key = read_varint(bytes, &mut offset)?;
64 let field = key >> 3;
65 let wire_type = key & 0x07;
66 if field == 0 || field > MAX_PROTOBUF_FIELD_NUMBER {
67 return Err(MintRootAssociationWireError::Malformed);
68 }
69 match field {
70 OWNER_MINT_ROOT_ATTACHMENT_FIELD => owner = true,
71 PASSKEY_MINT_ROOT_ATTACHMENT_FIELD => passkey = true,
72 _ => {}
73 }
74 if owner && passkey {
75 return Err(MintRootAssociationWireError::BothArms);
76 }
77 match wire_type {
78 0 => {
79 read_varint(bytes, &mut offset)?;
80 }
81 1 => skip(bytes, &mut offset, 8)?,
82 2 => {
83 let length = usize::try_from(read_varint(bytes, &mut offset)?)
84 .map_err(|_| MintRootAssociationWireError::Malformed)?;
85 skip(bytes, &mut offset, length)?;
86 }
87 5 => skip(bytes, &mut offset, 4)?,
88 _ => return Err(MintRootAssociationWireError::Malformed),
89 }
90 }
91 Ok(())
92}
93
94pub fn decode_thread_control_authority_for_verification(
96 bytes: &[u8],
97) -> Result<ThreadControlAuthority, MintRootAssociationWireError> {
98 verify_mint_root_association_wire(bytes)?;
99 Ok(ThreadControlAuthority::decode(bytes)?)
100}
101
102pub fn decode_spool_creation_proof_for_verification(
104 bytes: &[u8],
105) -> Result<SpoolCreationProof, MintRootAssociationWireError> {
106 verify_mint_root_association_wire(bytes)?;
107 Ok(SpoolCreationProof::decode(bytes)?)
108}