Skip to main content

telltale_runtime/heap/
encoding.rs

1//! Canonical heap encoding utilities.
2//!
3//! The runtime heap uses a heap-local canonical binary encoding.
4//! This encoding is deterministic, versioned, and complete for all
5//! semantically relevant fields in the current heap resource model.
6
7/// Heap encoding magic bytes.
8pub const HEAP_ENCODING_MAGIC: [u8; 4] = *b"TTHP";
9
10/// Heap encoding format version.
11pub const HEAP_ENCODING_VERSION: u16 = 1;
12
13const TAG_MESSAGE_PAYLOAD: u8 = 0x10;
14const TAG_CHANNEL_STATE: u8 = 0x20;
15const TAG_MESSAGE: u8 = 0x30;
16const TAG_RESOURCE_CHANNEL: u8 = 0x40;
17const TAG_RESOURCE_MESSAGE: u8 = 0x41;
18const TAG_RESOURCE_SESSION: u8 = 0x42;
19const TAG_RESOURCE_VALUE: u8 = 0x43;
20const TAG_RESOURCE_ID_PREIMAGE: u8 = 0x50;
21const TAG_RESOURCE_LEAF_PREIMAGE: u8 = 0x51;
22const TAG_NULLIFIER_LEAF_PREIMAGE: u8 = 0x52;
23const TAG_MERKLE_NODE_PREIMAGE: u8 = 0x53;
24const TAG_HEAP_COMMITMENT_PREIMAGE: u8 = 0x54;
25
26/// Canonical heap encoding boundary.
27///
28/// Types that participate in heap resource identity and commitments encode
29/// themselves through this trait.
30pub trait CanonicalHeapEncoding {
31    /// Append the canonical body for this value.
32    fn encode_canonical_body(&self, encoder: &mut CanonicalHeapEncoder);
33
34    /// The canonical tag for this value.
35    fn canonical_tag(&self) -> u8;
36
37    /// Encode this value to canonical bytes.
38    fn to_canonical_bytes(&self) -> Vec<u8> {
39        let mut encoder = CanonicalHeapEncoder::new(self.canonical_tag());
40        self.encode_canonical_body(&mut encoder);
41        encoder.finish()
42    }
43}
44
45/// Minimal binary encoder for canonical heap values.
46#[derive(Debug, Clone, Default)]
47pub struct CanonicalHeapEncoder {
48    bytes: Vec<u8>,
49}
50
51impl CanonicalHeapEncoder {
52    /// Create a new canonical encoder with the shared heap prelude.
53    pub fn new(tag: u8) -> Self {
54        let mut bytes = Vec::new();
55        bytes.extend_from_slice(&HEAP_ENCODING_MAGIC);
56        bytes.extend_from_slice(&HEAP_ENCODING_VERSION.to_le_bytes());
57        bytes.push(tag);
58        Self { bytes }
59    }
60
61    /// Append a nested canonical value.
62    pub fn nested<T: CanonicalHeapEncoding>(&mut self, value: &T) {
63        self.bytes.extend_from_slice(&value.to_canonical_bytes());
64    }
65
66    /// Append a UTF-8 string with a length prefix.
67    pub fn string(&mut self, value: &str) {
68        self.bytes
69            .extend_from_slice(&(value.len() as u32).to_le_bytes());
70        self.bytes.extend_from_slice(value.as_bytes());
71    }
72
73    /// Append a byte slice with a length prefix.
74    pub fn bytes(&mut self, value: &[u8]) {
75        self.bytes
76            .extend_from_slice(&(value.len() as u32).to_le_bytes());
77        self.bytes.extend_from_slice(value);
78    }
79
80    /// Append a `u32`.
81    pub fn u32(&mut self, value: u32) {
82        self.bytes.extend_from_slice(&value.to_le_bytes());
83    }
84
85    /// Append a `u64`.
86    pub fn u64(&mut self, value: u64) {
87        self.bytes.extend_from_slice(&value.to_le_bytes());
88    }
89
90    /// Finish encoding.
91    pub fn finish(self) -> Vec<u8> {
92        self.bytes
93    }
94}
95
96pub(crate) const fn tag_message_payload() -> u8 {
97    TAG_MESSAGE_PAYLOAD
98}
99
100pub(crate) const fn tag_channel_state() -> u8 {
101    TAG_CHANNEL_STATE
102}
103
104pub(crate) const fn tag_message() -> u8 {
105    TAG_MESSAGE
106}
107
108pub(crate) const fn tag_resource_channel() -> u8 {
109    TAG_RESOURCE_CHANNEL
110}
111
112pub(crate) const fn tag_resource_message() -> u8 {
113    TAG_RESOURCE_MESSAGE
114}
115
116pub(crate) const fn tag_resource_session() -> u8 {
117    TAG_RESOURCE_SESSION
118}
119
120pub(crate) const fn tag_resource_value() -> u8 {
121    TAG_RESOURCE_VALUE
122}
123
124/// Build the tagged preimage for `ResourceId` derivation.
125pub fn resource_id_preimage(resource_bytes: &[u8], counter: u64) -> Vec<u8> {
126    let mut encoder = CanonicalHeapEncoder::new(TAG_RESOURCE_ID_PREIMAGE);
127    encoder.bytes(resource_bytes);
128    encoder.u64(counter);
129    encoder.finish()
130}
131
132/// Build the tagged preimage for an active-resource Merkle leaf.
133pub fn resource_leaf_preimage(resource_id_bytes: &[u8], resource_bytes: &[u8]) -> Vec<u8> {
134    let mut encoder = CanonicalHeapEncoder::new(TAG_RESOURCE_LEAF_PREIMAGE);
135    encoder.bytes(resource_id_bytes);
136    encoder.bytes(resource_bytes);
137    encoder.finish()
138}
139
140/// Build the tagged preimage for a nullifier Merkle leaf.
141pub fn nullifier_leaf_preimage(resource_id_bytes: &[u8]) -> Vec<u8> {
142    let mut encoder = CanonicalHeapEncoder::new(TAG_NULLIFIER_LEAF_PREIMAGE);
143    encoder.bytes(resource_id_bytes);
144    encoder.finish()
145}
146
147/// Build the tagged preimage for a Merkle parent node.
148pub fn merkle_node_preimage(left: &[u8], right: &[u8]) -> Vec<u8> {
149    let mut encoder = CanonicalHeapEncoder::new(TAG_MERKLE_NODE_PREIMAGE);
150    encoder.bytes(left);
151    encoder.bytes(right);
152    encoder.finish()
153}
154
155/// Build the tagged preimage for `HeapCommitment::hash()`.
156pub fn heap_commitment_preimage(
157    resource_root: &[u8],
158    nullifier_root: &[u8],
159    counter: u64,
160) -> Vec<u8> {
161    let mut encoder = CanonicalHeapEncoder::new(TAG_HEAP_COMMITMENT_PREIMAGE);
162    encoder.bytes(resource_root);
163    encoder.bytes(nullifier_root);
164    encoder.u64(counter);
165    encoder.finish()
166}