miden-protocol 0.14.5

Core components of the Miden protocol
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
use miden::protocol::account_id
use miden::protocol::asset
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_ID_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_NONCE_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_INITIAL_COMMITMENT_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_COMPUTE_COMMITMENT_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_CODE_COMMITMENT_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_INITIAL_STORAGE_COMMITMENT_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_COMPUTE_STORAGE_COMMITMENT_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_INITIAL_VAULT_ROOT_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_VAULT_ROOT_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_ITEM_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_INITIAL_ITEM_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_MAP_ITEM_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_INITIAL_MAP_ITEM_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_ASSET_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_INITIAL_ASSET_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_NUM_PROCEDURES_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_GET_PROCEDURE_ROOT_OFFSET
use miden::protocol::kernel_proc_offsets::ACCOUNT_HAS_PROCEDURE_OFFSET
use miden::core::word

# ACTIVE ACCOUNT PROCEDURES
# =================================================================================================

# ERRORS
# -------------------------------------------------------------------------------------------------

const ERR_VAULT_GET_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET="get_balance can only be called on a fungible asset"

const ERR_VAULT_HAS_NON_FUNGIBLE_ASSET_PROC_CAN_BE_CALLED_ONLY_WITH_NON_FUNGIBLE_ASSET="the has_non_fungible_asset procedure can only be called on a non-fungible faucet"

# ID AND NONCE
# -------------------------------------------------------------------------------------------------

#! Returns the ID of the active account.
#!
#! Inputs:  []
#! Outputs: [account_id_suffix, account_id_prefix]
#!
#! Where:
#! - account_id_{suffix,prefix} are the suffix and prefix felts of the ID of the active account.
#!
#! Invocation: exec
pub proc get_id
    # pad the stack
    padw padw padw push.0.0
    # => [pad(14)]

    # push the flag indicating that the ID of the current account was requested
    push.0
    # => [is_native = 0, pad(14)]

    push.ACCOUNT_GET_ID_OFFSET
    # => [offset, is_native = 0, pad(14)]

    syscall.exec_kernel_proc
    # => [account_id_suffix, account_id_prefix, pad(14)]

    # clean the stack
    swapdw dropw dropw swapw dropw movdn.3 movdn.3 drop drop
    # => [account_id_suffix, account_id_prefix]
end

#! Returns the nonce of the active account.
#!
#! This procedure always returns the initial account nonce, as the nonce can only be incremented
#! in the authentication procedure when signing the transaction after all user code has been
#! executed.
#!
#! Inputs:  []
#! Outputs: [nonce]
#!
#! Where:
#! - nonce is the active account's nonce.
#!
#! Invocation: exec
pub proc get_nonce
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

    push.ACCOUNT_GET_NONCE_OFFSET
    # => [offset, pad(15)]

    syscall.exec_kernel_proc
    # => [nonce, pad(15)]

    # clean the stack
    swapdw dropw dropw swapw dropw movdn.3 drop drop drop
    # => [nonce]
end

# COMMITMENTS
# -------------------------------------------------------------------------------------------------

#! Returns the commitment of the active account at the beginning of the transaction.
#!
#! Inputs:  []
#! Outputs: [INIT_COMMITMENT]
#!
#! Where:
#! - INIT_COMMITMENT is the initial account commitment.
#!
#! Invocation: exec
pub proc get_initial_commitment
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

    push.ACCOUNT_GET_INITIAL_COMMITMENT_OFFSET
    # => [offset, pad(15)]

    syscall.exec_kernel_proc
    # => [INIT_COMMITMENT, pad(12)]

    # clean the stack
    swapdw dropw dropw swapw dropw
    # => [INIT_COMMITMENT]
end

#! Computes and returns commitment to the state of the active account.
#!
#! Inputs:  []
#! Outputs: [ACCOUNT_COMMITMENT]
#!
#! Where:
#! - ACCOUNT_COMMITMENT is the commitment of the account data.
#!
#! Invocation: exec
pub proc compute_commitment
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

    push.ACCOUNT_COMPUTE_COMMITMENT_OFFSET
    # => [offset, pad(15)]

    syscall.exec_kernel_proc
    # => [ACCOUNT_COMMITMENT, pad(12)]

    # clean the stack
    swapdw dropw dropw swapw dropw
    # => [ACCOUNT_COMMITMENT]
end

#! Gets the code commitment of the active account.
#!
#! Notice that the account code cannot be changed during the user code execution, so the code
#! commitment doesn't change during transaction execution: commitment returned by this procedure
#! could be used as both the initial and the current.
#!
#! The commitment to an empty delta is defined as the empty word.
#!
#! During an account-creating transaction (when the initial nonce is 0), this procedure will not
#! return the empty word even if the initial storage commitment and the current storage commitment
#! are identical (storage hasn't changed). This is because the delta for a new account must
#! represent its entire newly created state, and the initial storage in a transaction is
#! initialized to the the storage that the account ID commits to, which may be non-empty. This
#! does not have any consequences other than being inconsistent in this edge case.
#!
#! Inputs:  []
#! Outputs: [CODE_COMMITMENT]
#!
#! Where:
#! - CODE_COMMITMENT is the commitment of the account code.
#!
#! Invocation: exec
pub proc get_code_commitment
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

    push.ACCOUNT_GET_CODE_COMMITMENT_OFFSET
    # => [offset, pad(15)]

    syscall.exec_kernel_proc
    # => [CODE_COMMITMENT, pad(12)]

    # clean the stack
    swapdw dropw dropw swapw dropw
    # => [CODE_COMMITMENT]
end

#! Returns the storage commitment of the active account at the beginning of the transaction.
#!
#! During an account-creating transaction (when the initial nonce is 0), this procedure and
#! compute_storage_commitment will return the same value at the beginning of the transaction
#! (before any note or transaction scripts were executed). Despite that, the account delta may
#! not be empty. See account::compute_delta_commitment for more.
#!
#! Inputs:  []
#! Outputs: [INIT_STORAGE_COMMITMENT]
#!
#! Where:
#! - INIT_STORAGE_COMMITMENT is the initial account storage commitment.
#!
#! Invocation: exec
pub proc get_initial_storage_commitment
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

    push.ACCOUNT_GET_INITIAL_STORAGE_COMMITMENT_OFFSET
    # => [offset, pad(15)]

    syscall.exec_kernel_proc
    # => [INIT_STORAGE_COMMITMENT, pad(12)]

    # clean the stack
    swapdw dropw dropw swapw dropw
    # => [INIT_STORAGE_COMMITMENT]
end

#! Computes the latest storage commitment of the active account.
#!
#! Notice that this procedure always returns the latest commitment, but it doesn't actually always
#! recompute it: recomputation is performed only if the account's storage has been changed,
#! otherwise the cached value is returned.
#!
#! During an account-creating transaction (when the initial nonce is 0), this procedure and
#! get_initial_storage_commitment will return the same value at the beginning of the transaction
#! (before any note or transaction scripts were executed). Despite that, the account delta may
#! not be empty. See account::compute_delta_commitment for more.
#!
#! Inputs:  []
#! Outputs: [STORAGE_COMMITMENT]
#!
#! Where:
#! - STORAGE_COMMITMENT is the commitment of the account storage.
#!
#! Invocation: exec
pub proc compute_storage_commitment
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

    push.ACCOUNT_COMPUTE_STORAGE_COMMITMENT_OFFSET
    # => [offset, pad(15)]

    syscall.exec_kernel_proc
    # => [STORAGE_COMMITMENT, pad(12)]

    # clean the stack
    swapdw dropw dropw swapw dropw
    # => [STORAGE_COMMITMENT]
end

#! Returns the vault root of the active account at the beginning of the transaction.
#!
#! Inputs:  []
#! Outputs: [INIT_VAULT_ROOT]
#!
#! Where:
#! - INIT_VAULT_ROOT is the initial account vault root.
#!
#! Invocation: exec
pub proc get_initial_vault_root
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

    push.ACCOUNT_GET_INITIAL_VAULT_ROOT_OFFSET
    # => [offset, pad(15)]

    syscall.exec_kernel_proc
    # => [INIT_VAULT_ROOT, pad(12)]

    # clean the stack
    swapdw dropw dropw swapw dropw
    # => [INIT_VAULT_ROOT]
end

#! Returns the vault root of the active account.
#!
#! Inputs:  []
#! Outputs: [VAULT_ROOT]
#!
#! Where:
#! - VAULT_ROOT is the root of the account vault.
#!
#! Invocation: exec
pub proc get_vault_root
    # pad the stack for syscall invocation
    padw padw padw push.0.0.0
    # => [pad(15)]

    push.ACCOUNT_GET_VAULT_ROOT_OFFSET
    # => [offset, pad(15)]

    syscall.exec_kernel_proc
    # => [VAULT_ROOT, pad(12)]

    # clean the stack
    swapdw dropw dropw swapw dropw
    # => [VAULT_ROOT]
end

# STORAGE
# -------------------------------------------------------------------------------------------------

#! Gets an item from the active account storage.
#!
#! Inputs:  [slot_id_suffix, slot_id_prefix]
#! Outputs: [VALUE]
#!
#! Where:
#! - slot_id_{suffix, prefix} are the suffix and prefix felts of the slot identifier, which are
#!   the first two felts of the hashed slot name.
#! - VALUE is the value of the item.
#!
#! Panics if:
#! - a slot with the provided slot ID does not exist in account storage.
#!
#! Invocation: exec
pub proc get_item
    push.0 movdn.2
    # => [slot_id_suffix, slot_id_prefix, 0]

    push.ACCOUNT_GET_ITEM_OFFSET
    # => [offset, slot_id_suffix, slot_id_prefix, 0]

    # pad the stack
    padw swapw padw padw swapdw
    # => [offset, slot_id_suffix, slot_id_prefix, pad(13)]

    syscall.exec_kernel_proc
    # => [VALUE, pad(12)]

    # clean the stack
    swapdw dropw dropw swapw dropw
    # => [VALUE]
end

#! Gets the initial item from the active account storage slot as it was at the beginning of the
#! transaction.
#!
#! Inputs:  [slot_id_suffix, slot_id_prefix]
#! Outputs: [INIT_VALUE]
#!
#! Where:
#! - slot_id_{suffix, prefix} are the suffix and prefix felts of the slot identifier, which are
#!   the first two felts of the hashed slot name.
#! - INIT_VALUE is the initial value of the item at the beginning of the transaction.
#!
#! Panics if:
#! - a slot with the provided slot ID does not exist in account storage.
#!
#! Invocation: exec
pub proc get_initial_item
    push.0 movdn.2
    # => [slot_id_suffix, slot_id_prefix, 0]

    push.ACCOUNT_GET_INITIAL_ITEM_OFFSET
    # => [offset, slot_id_suffix, slot_id_prefix, 0]

    # pad the stack
    padw swapw padw padw swapdw
    # => [offset, slot_id_suffix, slot_id_prefix, pad(13)]

    syscall.exec_kernel_proc
    # => [INIT_VALUE, pad(12)]

    # clean the stack
    swapdw dropw dropw swapw dropw
    # => [INIT_VALUE]
end

#! Gets a map item from the active account storage.
#!
#! Inputs:  [slot_id_suffix, slot_id_prefix, KEY]
#! Outputs: [VALUE]
#!
#! Where:
#! - slot_id_{suffix, prefix} are the suffix and prefix felts of the slot identifier, which are
#!   the first two felts of the hashed slot name.
#!    - the slot must point to the root of the storage map.
#! - KEY is the key of the item to get.
#! - VALUE is the value of the item.
#!
#! Panics if:
#! - a slot with the provided slot ID does not exist in account storage.
#! - the slot item at index is not a map.
#!
#! Invocation: exec
pub proc get_map_item
    push.ACCOUNT_GET_MAP_ITEM_OFFSET
    # => [offset, slot_id_suffix, slot_id_prefix, KEY]

    # pad the stack
    push.0 movdn.7 padw padw swapdw
    # => [offset, slot_id_suffix, slot_id_prefix, KEY, pad(9)]

    syscall.exec_kernel_proc
    # => [VALUE, pad(12)]

    # clean the stack
    swapdw dropw dropw swapw dropw
    # => [VALUE]
end

#! Gets the initial VALUE from the active account storage map as it was at the beginning of the
#! transaction.
#!
#! Inputs:  [slot_id_suffix, slot_id_prefix, KEY]
#! Outputs: [INIT_VALUE]
#!
#! Where:
#! - slot_id_{suffix, prefix} are the suffix and prefix felts of the slot identifier, which are
#!   the first two felts of the hashed slot name.
#! - KEY is the key of the item to get.
#! - INIT_VALUE is the initial value of the item at the beginning of the transaction.
#!
#! Panics if:
#! - a slot with the provided slot ID does not exist in account storage.
#! - the slot item at index is not a map.
#!
#! Invocation: exec
pub proc get_initial_map_item
    push.ACCOUNT_GET_INITIAL_MAP_ITEM_OFFSET
    # => [offset, slot_id_suffix, slot_id_prefix, KEY]

    push.0 movdn.7 padw padw swapdw
    # => [offset, slot_id_suffix, slot_id_prefix, KEY, pad(9)]

    syscall.exec_kernel_proc
    # => [INIT_VALUE, pad(12)]

    # clean the stack
    swapdw dropw dropw swapw dropw
    # => [INIT_VALUE]
end

# VAULT
# -------------------------------------------------------------------------------------------------

#! Returns the asset associated with the provided asset vault key in the active account's vault.
#!
#! Inputs:  [ASSET_KEY]
#! Outputs: [ASSET_VALUE]
#!
#! Where:
#! - ASSET_KEY is the asset vault key of the asset to fetch.
#! - ASSET_VALUE is the value of the asset from the vault, which can be the EMPTY_WORD if it isn't
#!   present.
#!
#! Invocation: exec
pub proc get_asset
    push.ACCOUNT_GET_ASSET_OFFSET
    # => [offset, ASSET_KEY]

    # pad the stack
    push.0 movdn.5 push.0 movdn.5 push.0 movdn.5
    padw padw swapdw
    # => [offset, ASSET_KEY, pad(11)]

    syscall.exec_kernel_proc
    # => [ASSET_VALUE, pad(12)]

    # clean the stack
    swapdw dropw dropw swapw dropw
    # => [ASSET_VALUE]
end

#! Returns the asset associated with the provided asset vault key in the active account's vault at
#! the beginning of the transaction.
#!
#! Inputs:  [ASSET_KEY]
#! Outputs: [ASSET_VALUE]
#!
#! Where:
#! - ASSET_KEY is the asset vault key of the asset to fetch.
#! - ASSET_VALUE is the value of the asset from the vault, which can be the EMPTY_WORD if it isn't
#!   present.
#!
#! Invocation: exec
pub proc get_initial_asset
    push.ACCOUNT_GET_INITIAL_ASSET_OFFSET
    # => [offset, ASSET_KEY]

    # pad the stack
    push.0 movdn.5 push.0 movdn.5 push.0 movdn.5
    padw padw swapdw
    # => [offset, ASSET_KEY, pad(11)]

    syscall.exec_kernel_proc
    # => [ASSET_VALUE, pad(12)]

    # clean the stack
    swapdw dropw dropw swapw dropw
    # => [ASSET_VALUE]
end

#! Returns the balance of the fungible asset associated with the provided faucet_id in the active
#! account's vault.
#!
#! Inputs:  [faucet_id_suffix, faucet_id_prefix]
#! Outputs: [balance]
#!
#! Where:
#! - faucet_id_{suffix,prefix} are the suffix and prefix felts of the faucet ID of the fungible
#!   asset of interest.
#! - balance is the vault balance of the fungible asset.
#!
#! Panics if:
#! - the provided faucet ID is not an ID of a fungible faucet.
#!
#! Invocation: exec
pub proc get_balance
    # assert that the faucet id is a fungible faucet
    dup.1 exec.account_id::is_fungible_faucet
    assert.err=ERR_VAULT_GET_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET
    # => [faucet_id_suffix, faucet_id_prefix]

    # TODO(callbacks): This should take ASSET_KEY as input to avoid hardcoding the callbacks flag.
    push.0
    # => [enable_callbacks = 0, faucet_id_suffix, faucet_id_prefix]

    exec.asset::create_fungible_key
    # => [ASSET_KEY]

    exec.get_asset
    # => [ASSET_VALUE]

    # extract the asset's balance
    exec.::miden::protocol::util::asset::fungible_value_into_amount
    # => [balance]
end

#! Returns the balance of the fungible asset associated with the provided faucet_id in the active
#! account's vault at the beginning of the transaction.
#!
#! Inputs:  [faucet_id_suffix, faucet_id_prefix]
#! Outputs: [init_balance]
#!
#! Where:
#! - faucet_id_{suffix, prefix} are the suffix and prefix felts of the faucet id of the fungible
#!   asset of interest.
#! - init_balance is the vault balance of the fungible asset at the beginning of the transaction.
#!
#! Panics if:
#! - the provided faucet ID is not an ID of a fungible faucet.
#!
#! Invocation: exec
pub proc get_initial_balance
    # assert that the faucet id is a fungible faucet
    dup.1 exec.account_id::is_fungible_faucet
    assert.err=ERR_VAULT_GET_BALANCE_CAN_ONLY_BE_CALLED_ON_FUNGIBLE_ASSET
    # => [faucet_id_suffix, faucet_id_prefix]

    # TODO(callbacks): This should take ASSET_KEY as input to avoid hardcoding the callbacks flag.
    push.0
    # => [enable_callbacks = 0, faucet_id_suffix, faucet_id_prefix]

    exec.asset::create_fungible_key
    # => [ASSET_KEY]

    exec.get_initial_asset
    # => [ASSET_VALUE]

    # extract the asset's balance
    exec.::miden::protocol::util::asset::fungible_value_into_amount
    # => [balance]
end

#! Returns a boolean indicating whether the active account stores an asset with the provided
#! non-fungible asset vault key in its vault.
#!
#! Inputs:  [ASSET_KEY]
#! Outputs: [has_asset]
#!
#! Where:
#! - ASSET_KEY is the key of the fungible asset to check.
#! - has_asset is a boolean indicating whether the account vault has the asset.
#!
#! Panics if:
#! - the ASSET_VALUE is a fungible asset.
#!
#! Invocation: exec
pub proc has_non_fungible_asset
    # => [faucet_id_prefix, faucet_id_suffix, asset_id_prefix, asset_id_suffix]

    # assert that the faucet id is a non-fungible faucet
    dup.3 exec.account_id::is_non_fungible_faucet
    assert.err=ERR_VAULT_HAS_NON_FUNGIBLE_ASSET_PROC_CAN_BE_CALLED_ONLY_WITH_NON_FUNGIBLE_ASSET
    # => [ASSET_KEY]

    exec.get_asset
    # => [ASSET_VALUE]

    # compare with EMPTY_WORD to assess if the asset exists in the vault
    exec.word::eqz not
    # => [has_asset]
end

#! Returns the number of procedures in the active account.
#!
#! Inputs:  []
#! Outputs: [num_procedures]
#!
#! Where:
#! - num_procedures is the number of procedures in the active account.
#!
#! Invocation: exec
pub proc get_num_procedures
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

    push.ACCOUNT_GET_NUM_PROCEDURES_OFFSET
    # => [offset, pad(15)]

    syscall.exec_kernel_proc
    # => [num_procedures, pad(15)]

    # clean the stack
    swap.15 dropw dropw dropw drop drop drop
    # => [num_procedures]
end

#! Returns the procedure root for the procedure at the specified index in the active account.
#!
#! Inputs:  [index]
#! Outputs: [PROC_ROOT]
#!
#! Where:
#! - index is the index of the procedure.
#! - PROC_ROOT is the hash of the procedure.
#!
#! Panics if:
#! - the procedure index is out of bounds.
#!
#! Invocation: exec
pub proc get_procedure_root
    # => [index]

    push.0.0 movup.2
    push.ACCOUNT_GET_PROCEDURE_ROOT_OFFSET
    # => [offset, index, 0, 0]

    # pad the stack
    padw swapw padw padw swapdw
    # => [offset, index, pad(14)]

    syscall.exec_kernel_proc
    # => [PROC_ROOT, pad(12)]

    # clean the stack
    swapdw dropw dropw swapw dropw
    # => [PROC_ROOT]
end

#! Returns the binary flag indicating whether the procedure with the provided root is available on
#! the active account.
#!
#! Returns 1 if the procedure is available on the active account and 0 otherwise.
#!
#! Inputs:  [PROC_ROOT]
#! Outputs: [is_procedure_available]
#!
#! Where:
#! - PROC_ROOT is the hash of the procedure of interest.
#! - is_procedure_available is the binary flag indicating whether the procedure with PROC_ROOT is
#!   available on the active account.
#!
#! Invocation: exec
pub proc has_procedure
    push.ACCOUNT_HAS_PROCEDURE_OFFSET
    # => [offset, PROC_ROOT]

    # pad the stack
    push.0.0.0 movdn.7 movdn.7 movdn.7 padw padw swapdw
    # => [offset, PROC_ROOT, pad(11)]

    syscall.exec_kernel_proc
    # => [is_procedure_available, pad(15)]

    # clean the stack
    swapdw dropw dropw swapw dropw movdn.3 drop drop drop
    # => [is_procedure_available]
end