miden-protocol 0.17.0-rc.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
use {
    AccountId,
    AccountProcedureRoot,
    AssetId,
    BlockNumber,
    TransactionScriptRoot,
} from miden::protocol::types
use {TX_EXEC_FOREIGN_PROC_OFFSET, TX_GET_BLOCK_COMMITMENT_OFFSET, TX_GET_REFERENCE_BLOCK_NUMBER_OFFSET, TX_GET_BLOCK_TIMESTAMP_OFFSET, TX_GET_EXPIRATION_DELTA_OFFSET, TX_GET_FEE_ASSET_ID_OFFSET, TX_GET_INPUT_NOTES_COMMITMENT_OFFSET, TX_GET_NUM_INPUT_NOTES_OFFSET, TX_GET_NUM_OUTPUT_NOTES_OFFSET, TX_GET_OUTPUT_NOTES_COMMITMENT_OFFSET, TX_GET_TX_SCRIPT_ROOT_OFFSET, TX_PREPARE_FPI_OFFSET, TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET, TX_COMPUTE_FEE_OFFSET}
    from miden::protocol::kernel_proc_offsets

# RE-EXPORTS
# =================================================================================================

pub use {MAX_EXPIRATION_BLOCK_DELTA} from miden::protocol_utils::constants

#! Returns the block number of the transaction reference block.
#!
#! Inputs:  []
#! Outputs: [num]
#!
#! Where:
#! - num is the transaction reference block number.
#!
#! Invocation: exec
pub proc get_reference_block_number() -> BlockNumber
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

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

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

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

#! Returns the commitment of the block with the provided block number.
#!
#! Any block up to and including the transaction reference block can be read.
#!
#! Inputs:  [block_number]
#! Outputs: [BLOCK_COMMITMENT]
#!
#! Where:
#! - block_number is the number of the block whose commitment should be returned.
#! - BLOCK_COMMITMENT is the commitment of that block.
#!
#! Panics if:
#! - block_number is not a u32.
#! - block_number exceeds the transaction reference block number.
#!
#! Invocation: exec
pub proc get_block_commitment(block_number: BlockNumber) -> word
    padw padw padw push.0.0 movup.14
    # => [block_number, pad(14)]

    push.TX_GET_BLOCK_COMMITMENT_OFFSET
    # => [offset, block_number, pad(14)]

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

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

#! Returns the block commitment of the transaction reference block.
#!
#! Inputs:  []
#! Outputs: [BLOCK_COMMITMENT]
#!
#! Where:
#! - BLOCK_COMMITMENT is the commitment to the reference block of the transaction.
#!
#! Invocation: exec
pub proc get_reference_block_commitment() -> word
    exec.get_reference_block_number
    # => [reference_block_number]

    exec.get_block_commitment
    # => [BLOCK_COMMITMENT]
end

#! Returns the timestamp of the reference block for this transaction.
#!
#! WARNING: the returned timestamp is not guaranteed to be precise (i.e., could be several seconds
#! off) or recent, unless an expiration delta bounds the age of the reference block.
#!
#! Specifically, the reference blocks (and therefore the corresponding block timestamp) can be
#! chosen somewhat arbitrarily by the transaction executor. While this does not allow executors to
#! choose future timestamps, they can choose older timestamps for their benefit.
#!
#! For example, consider a script that includes a "time boundary", where before time 10 account X
#! can consume the note and after time 10 another account Y can consume the note. Even if the latest
#! block in the chain is at time 11, the owner of account X can choose to create a transaction
#! referencing the block at time 5 and still consume the note, while account Y would also be able
#! to consume the note when referencing the latest block. This is not necessarily a problem in all
#! cases, but must be taken into consideration by script developers.
#!
#! If the above is undesired, then one possible countermeasure is to set an expiration delta. For
#! example, with an expiration delta of 3, the oldest block account X could reference is the one at
#! time 8. This still allows for consumption by both accounts during a period of time, but shortens
#! that window.
#!
#! Inputs:  []
#! Outputs: [timestamp]
#!
#! Where:
#! - timestamp is the timestamp of the reference block for this transaction. The underlying value is
#!   of type u32, so u32 operations can be safely used on it.
pub proc get_block_timestamp() -> u32
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

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

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

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

#! Returns the input notes commitment hash.
#!
#! See `transaction::api::get_input_notes_commitment` for details.
#!
#! Inputs:  []
#! Outputs: [INPUT_NOTES_COMMITMENT]
#!
#! Where:
#! - INPUT_NOTES_COMMITMENT is the input notes commitment hash.
#!
#! Invocation: exec
pub proc get_input_notes_commitment() -> word
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

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

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

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

#! Returns the output notes commitment. This is computed as a sequential hash of
#! (note_details_commitment, note_metadata_commitment) tuples over all output notes.
#!
#! Inputs:  []
#! Outputs: [OUTPUT_NOTES_COMMITMENT]
#!
#! Where:
#! - OUTPUT_NOTES_COMMITMENT is the output notes commitment.
#!
#! Invocation: exec
pub proc get_output_notes_commitment() -> word
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

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

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

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

#! Returns the total number of input notes consumed by this transaction.
#!
#! Inputs:  []
#! Outputs: [num_input_notes]
#!
#! Where:
#! - num_input_notes is the total number of input notes consumed by this transaction.
#!
#! Invocation: exec
pub proc get_num_input_notes() -> u16
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

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

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

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

#! Returns the current number of output notes created in this transaction.
#!
#! The number of output notes can changes during transaction execution. This will happen any time
#! as new output notes is created.
#!
#! Inputs:  []
#! Outputs: [num_output_notes]
#!
#! Where:
#! - num_output_notes is the number of output notes created in this transaction so far.
#!
#! Invocation: exec
pub proc get_num_output_notes() -> u16
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

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

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

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

#! Executes the provided procedure against the foreign account.
#!
#! WARNING: the foreign account state read here reflects the transaction reference block, which is
#! chosen by the executor. The foreign account commitment is not a transaction public input
#! and is not revalidated against the foreign account's current on-chain state at inclusion, so the
#! returned values may be outdated.
#!
#! Any FPI-callable procedure or asset callback that reads mutable, security-sensitive state must
#! call [`tx::update_expiration_block_delta`] in the execution path that reads that state. This is
#! the foreign account's responsibility because the caller chooses the transaction reference block.
#! No expiration delta is required when a procedure reads only immutable data or when stale data is
#! acceptable.
#!
#! The expiration delta bounds the age of the reference block relative to the block that includes
#! the transaction. For example, if an oracle price is updated every 5 blocks, the oracle account
#! should set an expiration delta of 5 or less. If the current block is 40 and the delta is 5, the
#! reference block must be block 35 or newer, so a value from block 20 cannot be read.
#!
#! Inputs:  [foreign_account_id_suffix, foreign_account_id_prefix, FOREIGN_PROC_ROOT, foreign_procedure_inputs(16)]
#! Outputs: [foreign_procedure_outputs(16)]
#!
#! Where:
#! - foreign_account_id_{suffix,prefix} are the suffix and prefix felts of the account ID of the
#!   foreign account to execute the procedure on.
#! - foreign_procedure_inputs are the inputs to the foreign procedure padded to 16 felts.
#! - foreign_procedure_outputs are the outputs of the foreign procedure padded to 16 felts.
#!
#! Panics if:
#! - the provided foreign account ID is invalid.
#! - FOREIGN_PROC_ROOT is not part of the foreign account's code.
#!
#! Invocation: exec
@locals(6) # foreign proc root (4) + foreign account ID (2)
pub proc execute_foreign_procedure(
    foreign_account_id: AccountId,
    foreign_proc_root: AccountProcedureRoot,
    ...
) -> ...
    # store the foreign account ID and foreign procedure root to the local memory
    # this will allow us to get the 16th element of the foreign procedure inputs to pass it to the
    # `tx_prepare_fpi` kernel procedure
    loc_store.4 loc_store.5 loc_storew_le.0 dropw
    # OS => [foreign_procedure_inputs(16)]
    # LM => [FOREIGN_PROC_ROOT, foreign_account_id_suffix, foreign_account_id_prefix]

    # move up the last element of the foreign procedure inputs
    movup.15
    # => [foreign_proc_input_value_15, foreign_procedure_inputs(15)]

    # load the foreign account ID and foreign procedure root back to the operand stack
    padw loc_loadw_le.0 loc_load.5 loc_load.4
    # => [foreign_account_id_suffix, foreign_account_id_prefix, FOREIGN_PROC_ROOT, foreign_proc_input_value_15, foreign_procedure_inputs(15)]

    # get the tx_prepare_fpi procedure offset
    push.TX_PREPARE_FPI_OFFSET
    # => [offset, foreign_account_id_suffix, foreign_account_id_prefix, FOREIGN_PROC_ROOT, foreign_proc_input_value_15, foreign_procedure_inputs(15)]

    # pad the stack before the syscall
    padw padw swapdw
    # => [offset, foreign_account_id_suffix, foreign_account_id_prefix, FOREIGN_PROC_ROOT,
    #     foreign_proc_input_value_15, pad(8), foreign_procedure_inputs(15)]

    # store the foreign account ID, foreign procedure root, and the 16th (last) element of the
    # foreign procedure inputs to the memory
    syscall.exec_kernel_proc
    # => [pad(16), foreign_procedure_inputs(15)]

    # clean the stack
    dropw dropw dropw dropw
    # => [foreign_procedure_inputs(15)]

    # perform the FPI call
    push.TX_EXEC_FOREIGN_PROC_OFFSET syscall.exec_kernel_proc
    # => [foreign_procedure_outputs(16)]
end

#! Updates the expiration delta.
#!
#! The expiration delta specifies how close to the transaction's reference block the transaction
#! must be included in the chain. For example, if the transaction's reference block is 100 and the
#! expiration delta is 10, the transaction can be included in the chain by block 110. If this does
#! not happen, the transaction is considered expired and cannot be included in the chain.
#!
#! Once set, the expiration delta can be decreased, but not increased.
#!
#! Inputs: [block_height_delta, ...]
#! Output: [...]
#!
#! Where:
#! - block_height_delta is the desired expiration delta (1 to 65535).
#!
#! Annotation hint: is not used anywhere
pub proc update_expiration_block_delta(block_height_delta: u16)
    push.TX_UPDATE_EXPIRATION_BLOCK_DELTA_OFFSET
    # => [offset, expiration_delta, ...]

    # pad the stack
    push.0 movdn.2 push.0 movdn.2 padw swapw padw padw swapdw
    # => [offset, expiration_delta, pad(14)]

    syscall.exec_kernel_proc

    # clear the stack
    dropw dropw dropw dropw
end

#! Returns the expiration delta, or 0 if the delta has not been set.
#!
#! Inputs: [...]
#! Output: [block_height_delta, ...]
#!
#! Where:
#! - block_height_delta is the stored expiration delta (1 to 65535).
#!
#! Annotation hint: is not used anywhere
pub proc get_expiration_block_delta() -> u16
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

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

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

    # clear the stack
    swapw.3 dropw dropw dropw movdn.3 drop drop drop
    # => [expiration_delta]
end

#! Returns the transaction script root, or the empty word if no transaction script was executed.
#!
#! Inputs:  []
#! Outputs: [TX_SCRIPT_ROOT]
#!
#! Where:
#! - TX_SCRIPT_ROOT is the root of the transaction script executed in this transaction, or the
#!   empty word if no transaction script was executed.
#!
#! Invocation: exec
pub proc get_tx_script_root() -> TransactionScriptRoot
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

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

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

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

#! Computes the fee required for the current transaction.
#!
#! Inputs:  [num_extra_cycles, EXCLUDE_NOTES_COMMITMENT]
#! Outputs: [fee_amount]
#!
#! Where:
#! - num_extra_cycles is the number of cycles that should be added to the current number of cycles
#!   before computing the fee. This allows accounting for cycles that are spent after this call, e.g.
#!   for signature verification or the epilogue.
#! - EXCLUDE_NOTES_COMMITMENT is a commitment to a list of output note indices that should be
#!   excluded from the fee computation, or the empty word if no notes are excluded.
#! - fee_amount is the computed fee amount of the transaction in the fee asset.
#!
#! Invocation: exec
pub proc compute_fee(num_extra_cycles: u32, exclude_notes_commitment: word) -> felt
    push.TX_COMPUTE_FEE_OFFSET
    # => [offset, num_extra_cycles, EXCLUDE_NOTES_COMMITMENT]

    # pad the stack to 16 elements
    push.0.0 movdn.7 movdn.7 padw padw swapdw
    # => [offset, num_extra_cycles, EXCLUDE_NOTES_COMMITMENT, pad(10)]

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

    # truncate the stack
    movdn.15 dropw dropw dropw drop drop drop
    # => [fee_amount]
end

#! Returns the fee asset ID of the transaction's reference block.
#!
#! Inputs:  []
#! Outputs: [FEE_ASSET_ID]
#!
#! Where:
#! - FEE_ASSET_ID is the ID of the asset that fees are paid in.
#!
#! Invocation: exec
pub proc get_fee_asset_id() -> AssetId
    # pad the stack
    padw padw padw push.0.0.0
    # => [pad(15)]

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

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

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