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 both the note attachment and the metadata
112/// header as separate words.
113#[derive(Copy, Clone, Debug, PartialEq, Eq)]
114#[repr(C)]
115pub struct NoteMetadata {
116    /// The attachment of the note.
117    pub attachment: Word,
118    /// The metadata header of the note.
119    pub header: Word,
120}
121
122impl NoteMetadata {
123    /// Creates a new [`NoteMetadata`] from attachment and header.
124    pub fn new(attachment: Word, header: Word) -> Self {
125        Self { attachment, header }
126    }
127}
128
129/// Result of searching note metadata for an attachment scheme.
130#[derive(Copy, Clone, Debug, PartialEq, Eq)]
131#[repr(C)]
132pub struct AttachmentLocation {
133    /// Non-zero when the attachment scheme was found.
134    pub is_found: Felt,
135    /// The matching attachment index, valid only when `is_found` is non-zero.
136    pub index: Felt,
137}
138
139impl AttachmentLocation {
140    /// Returns whether the attachment scheme was found.
141    #[inline]
142    pub fn found(&self) -> bool {
143        self.is_found != Felt::new(0).unwrap()
144    }
145}
146
147impl From<[Felt; 4]> for Recipient {
148    fn from(value: [Felt; 4]) -> Self {
149        Recipient {
150            inner: Word::from(value),
151        }
152    }
153}
154
155impl From<Word> for Recipient {
156    fn from(value: Word) -> Self {
157        Recipient { inner: value }
158    }
159}
160
161impl From<Recipient> for Word {
162    #[inline]
163    fn from(value: Recipient) -> Self {
164        value.inner
165    }
166}
167
168#[derive(Clone, Copy, Debug, PartialEq, Eq)]
169#[repr(transparent)]
170pub struct Tag {
171    pub inner: Felt,
172}
173
174impl From<Felt> for Tag {
175    fn from(value: Felt) -> Self {
176        Tag { inner: value }
177    }
178}
179
180impl From<Tag> for Word {
181    #[inline]
182    fn from(value: Tag) -> Self {
183        padded_word_from_felt(value.inner)
184    }
185}
186
187impl TryFrom<Word> for Tag {
188    type Error = &'static str;
189
190    #[inline]
191    fn try_from(value: Word) -> Result<Self, Self::Error> {
192        Ok(Tag {
193            inner: felt_from_padded_word(value)?,
194        })
195    }
196}
197
198#[derive(Clone, Copy, Debug, PartialEq, Eq)]
199#[repr(transparent)]
200pub struct NoteIdx {
201    pub inner: Felt,
202}
203
204impl From<NoteIdx> for Word {
205    #[inline]
206    fn from(value: NoteIdx) -> Self {
207        padded_word_from_felt(value.inner)
208    }
209}
210
211impl TryFrom<Word> for NoteIdx {
212    type Error = &'static str;
213
214    #[inline]
215    fn try_from(value: Word) -> Result<Self, Self::Error> {
216        Ok(NoteIdx {
217            inner: felt_from_padded_word(value)?,
218        })
219    }
220}
221
222#[derive(Clone, Copy, Debug, PartialEq, Eq)]
223#[repr(transparent)]
224pub struct NoteType {
225    pub inner: Felt,
226}
227
228impl From<Felt> for NoteType {
229    fn from(value: Felt) -> Self {
230        NoteType { inner: value }
231    }
232}
233
234impl From<NoteType> for Word {
235    #[inline]
236    fn from(value: NoteType) -> Self {
237        padded_word_from_felt(value.inner)
238    }
239}
240
241impl TryFrom<Word> for NoteType {
242    type Error = &'static str;
243
244    #[inline]
245    fn try_from(value: Word) -> Result<Self, Self::Error> {
246        Ok(NoteType {
247            inner: felt_from_padded_word(value)?,
248        })
249    }
250}
251
252/// The partial hash of a storage slot name.
253///
254/// A slot id consists of two field elements: a `prefix` and a `suffix`.
255///
256/// Slot ids uniquely identify slots in account storage and are used by the host functions exposed
257/// via `miden::protocol::*`.
258#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
259pub struct StorageSlotId {
260    suffix: Felt,
261    prefix: Felt,
262}
263
264impl StorageSlotId {
265    /// Creates a new [`StorageSlotId`] from the provided felts.
266    ///
267    /// Note: this constructor takes `(suffix, prefix)` to match the values returned by
268    /// `miden_protocol::account::StorageSlotId::{suffix,prefix}`.
269    pub fn new(suffix: Felt, prefix: Felt) -> Self {
270        Self { suffix, prefix }
271    }
272
273    /// Creates a new [`StorageSlotId`] from the provided felts in host-call order.
274    ///
275    /// Host functions take the `prefix` first and then the `suffix`.
276    pub fn from_prefix_suffix(prefix: Felt, suffix: Felt) -> Self {
277        Self { suffix, prefix }
278    }
279
280    /// Returns the `(prefix, suffix)` pair in host-call order.
281    pub fn to_prefix_suffix(&self) -> (Felt, Felt) {
282        (self.prefix, self.suffix)
283    }
284
285    /// Returns the `(suffix, prefix)` pair in storage-slot order.
286    pub fn to_suffix_prefix(&self) -> (Felt, Felt) {
287        (self.suffix, self.prefix)
288    }
289
290    /// Returns the suffix of the [`StorageSlotId`].
291    pub fn suffix(&self) -> Felt {
292        self.suffix
293    }
294
295    /// Returns the prefix of the [`StorageSlotId`].
296    pub fn prefix(&self) -> Felt {
297        self.prefix
298    }
299}
300
301#[cfg(test)]
302mod tests {
303    use miden_stdlib_sys::{Word, felt};
304
305    use super::{felt_from_padded_word, padded_word_from_felt};
306
307    /// Ensures `padded_word_from_felt` zero-pads the trailing three limbs.
308    #[test]
309    fn padded_word_from_felt_zero_pads_trailing_limbs() {
310        assert_eq!(
311            padded_word_from_felt(felt!(7)),
312            Word::new([felt!(7), felt!(0), felt!(0), felt!(0)])
313        );
314    }
315
316    /// Ensures `felt_from_padded_word` rejects words with non-zero trailing padding.
317    #[test]
318    fn felt_from_padded_word_rejects_non_zero_padding() {
319        let err =
320            felt_from_padded_word(Word::new([felt!(7), felt!(1), felt!(0), felt!(0)])).unwrap_err();
321
322        assert_eq!(err, "expected zero padding in the trailing three felts");
323    }
324
325    /// Ensures the felt-padding helpers form a lossless roundtrip for scalar values.
326    #[test]
327    fn felt_padding_helpers_roundtrip() {
328        let value = felt!(42);
329
330        assert_eq!(felt_from_padded_word(padded_word_from_felt(value)), Ok(value));
331    }
332}