Skip to main content

miden_base_sys/bindings/
types.rs

1extern crate alloc;
2
3use miden_field_repr::FromFeltRepr;
4use miden_stdlib_sys::{Felt, Word, felt};
5
6/// Packs a scalar felt into the leading limb of a protocol word.
7pub fn padded_word_from_felt(value: Felt) -> Word {
8    Word::new([value, felt!(0), felt!(0), felt!(0)])
9}
10
11/// Extracts a scalar felt from a protocol word with zero-padded trailing limbs.
12pub fn felt_from_padded_word(value: Word) -> Result<Felt, &'static str> {
13    if value[1] != felt!(0) || value[2] != felt!(0) || value[3] != felt!(0) {
14        return Err("expected zero padding in the trailing three felts");
15    }
16
17    Ok(value[0])
18}
19
20/// Unique identifier for a Miden account, composed of two field elements.
21#[derive(Copy, Clone, Debug, PartialEq, Eq, FromFeltRepr)]
22pub struct AccountId {
23    pub prefix: Felt,
24    pub suffix: Felt,
25}
26
27impl AccountId {
28    /// Creates a new AccountId from prefix and suffix Felt values.
29    pub fn new(prefix: Felt, suffix: Felt) -> Self {
30        Self { prefix, suffix }
31    }
32}
33
34/// Raw protocol return layout for account identifiers.
35/// The protocol MASM procedures are returning [suffix, prefix]
36#[derive(Copy, Clone)]
37#[repr(C)]
38pub(crate) struct RawAccountId {
39    pub suffix: Felt,
40    pub prefix: Felt,
41}
42
43impl RawAccountId {
44    /// Converts the protocol return layout into the Rust [`AccountId`] layout.
45    pub(crate) fn into_account_id(self) -> AccountId {
46        AccountId::new(self.prefix, self.suffix)
47    }
48}
49
50impl From<AccountId> for Word {
51    #[inline]
52    fn from(value: AccountId) -> Self {
53        Word::from([felt!(0), felt!(0), value.suffix, value.prefix])
54    }
55}
56
57impl TryFrom<Word> for AccountId {
58    type Error = &'static str;
59
60    #[inline]
61    fn try_from(value: Word) -> Result<Self, Self::Error> {
62        if value[0] != felt!(0) || value[1] != felt!(0) {
63            return Err("expected zero padding in the upper two felts");
64        }
65
66        Ok(Self {
67            prefix: value[3],
68            suffix: value[2],
69        })
70    }
71}
72
73/// A fungible or non-fungible asset encoded as separate vault key and value words.
74///
75/// The `key` identifies the asset in the account vault and the `value` stores the corresponding
76/// asset contents. This matches the v0.14 protocol/base ABI.
77#[derive(Copy, Clone, Debug, PartialEq, Eq)]
78#[repr(C)]
79pub struct Asset {
80    /// The asset's vault key.
81    pub key: Word,
82    /// The asset's vault value.
83    pub value: Word,
84}
85
86impl Asset {
87    /// Creates a new [`Asset`] from its key and value words.
88    pub fn new(key: impl Into<Word>, value: impl Into<Word>) -> Self {
89        Self {
90            key: key.into(),
91            value: value.into(),
92        }
93    }
94}
95
96impl From<Asset> for (Word, Word) {
97    fn from(val: Asset) -> Self {
98        (val.key, val.value)
99    }
100}
101
102/// A note recipient digest.
103#[derive(Clone, Debug, PartialEq, Eq)]
104#[repr(transparent)]
105pub struct Recipient {
106    pub inner: Word,
107}
108
109/// The note metadata returned by `*_note::get_metadata` procedures.
110///
111/// In the Miden protocol, metadata retrieval returns a single metadata header word. Note
112/// attachments are retrieved separately via the `*_note::get_attachments_commitment`,
113/// `find_attachment`, and `write_attachment_*` procedures.
114#[derive(Copy, Clone, Debug, PartialEq, Eq)]
115#[repr(C)]
116pub struct NoteMetadata {
117    /// The metadata header of the note.
118    pub header: Word,
119}
120
121impl NoteMetadata {
122    /// Creates a new [`NoteMetadata`] from the metadata header word.
123    pub fn new(header: Word) -> Self {
124        Self { header }
125    }
126}
127
128/// Result of searching note metadata for an attachment scheme.
129#[derive(Copy, Clone, Debug, PartialEq, Eq)]
130#[repr(C)]
131pub struct AttachmentLocation {
132    /// Non-zero when the attachment scheme was found.
133    pub is_found: Felt,
134    /// The matching attachment index, valid only when `is_found` is non-zero.
135    pub index: Felt,
136}
137
138impl AttachmentLocation {
139    /// Returns whether the attachment scheme was found.
140    #[inline]
141    pub fn found(&self) -> bool {
142        self.is_found != Felt::new(0).unwrap()
143    }
144}
145
146impl From<[Felt; 4]> for Recipient {
147    fn from(value: [Felt; 4]) -> Self {
148        Recipient {
149            inner: Word::from(value),
150        }
151    }
152}
153
154impl From<Word> for Recipient {
155    fn from(value: Word) -> Self {
156        Recipient { inner: value }
157    }
158}
159
160impl From<Recipient> for Word {
161    #[inline]
162    fn from(value: Recipient) -> Self {
163        value.inner
164    }
165}
166
167#[derive(Clone, Copy, Debug, PartialEq, Eq)]
168#[repr(transparent)]
169pub struct Tag {
170    pub inner: Felt,
171}
172
173impl From<Felt> for Tag {
174    fn from(value: Felt) -> Self {
175        Tag { inner: value }
176    }
177}
178
179impl From<Tag> for Word {
180    #[inline]
181    fn from(value: Tag) -> Self {
182        padded_word_from_felt(value.inner)
183    }
184}
185
186impl TryFrom<Word> for Tag {
187    type Error = &'static str;
188
189    #[inline]
190    fn try_from(value: Word) -> Result<Self, Self::Error> {
191        Ok(Tag {
192            inner: felt_from_padded_word(value)?,
193        })
194    }
195}
196
197#[derive(Clone, Copy, Debug, PartialEq, Eq)]
198#[repr(transparent)]
199pub struct NoteIdx {
200    pub inner: Felt,
201}
202
203impl From<NoteIdx> for Word {
204    #[inline]
205    fn from(value: NoteIdx) -> Self {
206        padded_word_from_felt(value.inner)
207    }
208}
209
210impl TryFrom<Word> for NoteIdx {
211    type Error = &'static str;
212
213    #[inline]
214    fn try_from(value: Word) -> Result<Self, Self::Error> {
215        Ok(NoteIdx {
216            inner: felt_from_padded_word(value)?,
217        })
218    }
219}
220
221#[derive(Clone, Copy, Debug, PartialEq, Eq)]
222#[repr(transparent)]
223pub struct NoteType {
224    pub inner: Felt,
225}
226
227impl From<Felt> for NoteType {
228    fn from(value: Felt) -> Self {
229        NoteType { inner: value }
230    }
231}
232
233impl From<NoteType> for Word {
234    #[inline]
235    fn from(value: NoteType) -> Self {
236        padded_word_from_felt(value.inner)
237    }
238}
239
240impl TryFrom<Word> for NoteType {
241    type Error = &'static str;
242
243    #[inline]
244    fn try_from(value: Word) -> Result<Self, Self::Error> {
245        Ok(NoteType {
246            inner: felt_from_padded_word(value)?,
247        })
248    }
249}
250
251/// The partial hash of a storage slot name.
252///
253/// A slot id consists of two field elements: a `prefix` and a `suffix`.
254///
255/// Slot ids uniquely identify slots in account storage and are used by the host functions exposed
256/// via `miden::protocol::*`.
257#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
258pub struct StorageSlotId {
259    suffix: Felt,
260    prefix: Felt,
261}
262
263impl StorageSlotId {
264    /// Creates a new [`StorageSlotId`] from the provided felts.
265    ///
266    /// Note: this constructor takes `(suffix, prefix)` to match the values returned by
267    /// `miden_protocol::account::StorageSlotId::{suffix,prefix}`.
268    pub fn new(suffix: Felt, prefix: Felt) -> Self {
269        Self { suffix, prefix }
270    }
271
272    /// Creates a new [`StorageSlotId`] from the provided felts in host-call order.
273    ///
274    /// Host functions take the `prefix` first and then the `suffix`.
275    pub fn from_prefix_suffix(prefix: Felt, suffix: Felt) -> Self {
276        Self { suffix, prefix }
277    }
278
279    /// Returns the `(prefix, suffix)` pair in host-call order.
280    pub fn to_prefix_suffix(&self) -> (Felt, Felt) {
281        (self.prefix, self.suffix)
282    }
283
284    /// Returns the `(suffix, prefix)` pair in storage-slot order.
285    pub fn to_suffix_prefix(&self) -> (Felt, Felt) {
286        (self.suffix, self.prefix)
287    }
288
289    /// Returns the suffix of the [`StorageSlotId`].
290    pub fn suffix(&self) -> Felt {
291        self.suffix
292    }
293
294    /// Returns the prefix of the [`StorageSlotId`].
295    pub fn prefix(&self) -> Felt {
296        self.prefix
297    }
298}
299
300#[cfg(test)]
301mod tests {
302    use miden_stdlib_sys::{Word, felt};
303
304    use super::{felt_from_padded_word, padded_word_from_felt};
305
306    /// Ensures `padded_word_from_felt` zero-pads the trailing three limbs.
307    #[test]
308    fn padded_word_from_felt_zero_pads_trailing_limbs() {
309        assert_eq!(
310            padded_word_from_felt(felt!(7)),
311            Word::new([felt!(7), felt!(0), felt!(0), felt!(0)])
312        );
313    }
314
315    /// Ensures `felt_from_padded_word` rejects words with non-zero trailing padding.
316    #[test]
317    fn felt_from_padded_word_rejects_non_zero_padding() {
318        let err =
319            felt_from_padded_word(Word::new([felt!(7), felt!(1), felt!(0), felt!(0)])).unwrap_err();
320
321        assert_eq!(err, "expected zero padding in the trailing three felts");
322    }
323
324    /// Ensures the felt-padding helpers form a lossless roundtrip for scalar values.
325    #[test]
326    fn felt_padding_helpers_roundtrip() {
327        let value = felt!(42);
328
329        assert_eq!(felt_from_padded_word(padded_word_from_felt(value)), Ok(value));
330    }
331}