• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Neptune-Crypto / neptune-core / 14083102973

26 Mar 2025 12:18PM UTC coverage: 84.219% (-0.06%) from 84.277%
14083102973

push

github

Sword-Smith
ci: Synchronize pre-commit hook with CI

Catch more stuff locally!

Co-authored-by: Alan Szepieniec <alan@neptune.cash>

50934 of 60478 relevant lines covered (84.22%)

174106.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.16
/src/models/blockchain/type_scripts/time_lock.rs
1
use std::collections::HashMap;
2
use std::sync::OnceLock;
3

4
use get_size2::GetSize;
5
use itertools::Itertools;
6
use num_traits::Zero;
7
use serde::Deserialize;
8
use serde::Serialize;
9
use tasm_lib::memory::encode_to_memory;
10
use tasm_lib::memory::FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS;
11
use tasm_lib::prelude::Digest;
12
use tasm_lib::prelude::Library;
13
use tasm_lib::structure::verify_nd_si_integrity::VerifyNdSiIntegrity;
14
use tasm_lib::triton_vm::prelude::*;
15
use tasm_lib::twenty_first::math::b_field_element::BFieldElement;
16
use tasm_lib::twenty_first::math::bfield_codec::BFieldCodec;
17

18
use super::TypeScript;
19
use super::TypeScriptWitness;
20
use crate::models::blockchain::transaction::primitive_witness::PrimitiveWitness;
21
use crate::models::blockchain::transaction::primitive_witness::SaltedUtxos;
22
use crate::models::blockchain::transaction::transaction_kernel::TransactionKernel;
23
use crate::models::blockchain::transaction::transaction_kernel::TransactionKernelField;
24
use crate::models::blockchain::transaction::utxo::Coin;
25
use crate::models::blockchain::transaction::utxo::Utxo;
26
use crate::models::blockchain::type_scripts::TypeScriptAndWitness;
27
use crate::models::proof_abstractions::mast_hash::MastHash;
28
use crate::models::proof_abstractions::tasm::program::ConsensusProgram;
29
use crate::models::proof_abstractions::timestamp::Timestamp;
30
use crate::models::proof_abstractions::SecretWitness;
31

32
#[derive(Debug, Copy, Clone, Deserialize, Serialize, BFieldCodec, GetSize, PartialEq, Eq)]
×
33
pub struct TimeLock;
34

35
impl TimeLock {
36
    /// Create a `TimeLock` type-script-and-state-pair that releases the coins at the
37
    /// given release date, which corresponds to the number of milliseconds that passed
38
    /// since the unix epoch started (00:00 am UTC on Jan 1 1970).
39
    pub fn until(date: Timestamp) -> Coin {
272,717✔
40
        Coin {
272,717✔
41
            type_script_hash: TimeLock.hash(),
272,717✔
42
            state: vec![date.0],
272,717✔
43
        }
272,717✔
44
    }
272,717✔
45

46
    /// Get the release date from a `Utxo`, if any. If there aren't any, return
47
    /// the null release date.
48
    pub fn extract_release_date(utxo: &Utxo) -> Timestamp {
1,756✔
49
        utxo.coins()
1,756✔
50
            .iter()
1,756✔
51
            .find_map(Coin::release_date)
1,756✔
52
            .unwrap_or_else(Timestamp::zero)
1,756✔
53
    }
1,756✔
54
}
55

56
impl ConsensusProgram for TimeLock {
57
    fn library_and_code(&self) -> (Library, Vec<LabelledInstruction>) {
3,938✔
58
        let (library, audit_preloaded_data, audit_subroutine) = {
3,938✔
59
            let mut library = Library::new();
3,938✔
60
            let audit_preloaded_data = library.import(Box::new(VerifyNdSiIntegrity::<
3,938✔
61
                TimeLockWitnessMemory,
3,938✔
62
            >::default()));
3,938✔
63
            let audit_subroutine = library.all_imports();
3,938✔
64
            (library, audit_preloaded_data, audit_subroutine)
3,938✔
65
        };
3,938✔
66

3,938✔
67
        // Generated by tasm-lang compiler
3,938✔
68
        // `tasm-lang typescript-timelock.rs type-script-time-lock.tasm`
3,938✔
69
        // 2024-09-09
3,938✔
70
        let code = triton_asm! {
3,938✔
71
        call main
3,938✔
72
        halt
3,938✔
73
        {&audit_subroutine}
3,938✔
74

3,938✔
75
        main:
3,938✔
76
        call tasmlib_verifier_own_program_digest
3,938✔
77
        dup 4
3,938✔
78
        dup 4
3,938✔
79
        dup 4
3,938✔
80
        dup 4
3,938✔
81
        dup 4
3,938✔
82
        push -6
3,938✔
83
        write_mem 5
3,938✔
84
        pop 1
3,938✔
85
        hint self_digest = stack[0..5]
3,938✔
86
        call tasmlib_io_read_stdin___digest
3,938✔
87
        dup 4
3,938✔
88
        dup 4
3,938✔
89
        dup 4
3,938✔
90
        dup 4
3,938✔
91
        dup 4
3,938✔
92
        push -11
3,938✔
93
        write_mem 5
3,938✔
94
        pop 1
3,938✔
95
        hint tx_kernel_digest = stack[0..5]
3,938✔
96
        call tasmlib_io_read_stdin___digest
3,938✔
97
        dup 4
3,938✔
98
        dup 4
3,938✔
99
        dup 4
3,938✔
100
        dup 4
3,938✔
101
        dup 4
3,938✔
102
        push -16
3,938✔
103
        write_mem 5
3,938✔
104
        pop 1
3,938✔
105
        hint input_utxos_digest = stack[0..5]
3,938✔
106
        call tasmlib_io_read_stdin___digest
3,938✔
107
        hint _output_utxos_digest = stack[0..5]
3,938✔
108
        push 5
3,938✔
109
        hint leaf_index = stack[0]
3,938✔
110
        call tasmlib_io_read_secin___bfe
3,938✔
111
        dup 0
3,938✔
112
        push -17
3,938✔
113
        write_mem 1
3,938✔
114
        pop 1
3,938✔
115
        hint timestamp = stack[0]
3,938✔
116
        push -17
3,938✔
117
        read_mem 1
3,938✔
118
        pop 1
3,938✔
119
        call encode_BField
3,938✔
120
        call tasm_langs_hash_varlen
3,938✔
121
        hint leaf = stack[0..5]
3,938✔
122
        push 3
3,938✔
123
        hint tree_height = stack[0]
3,938✔
124
        push -7
3,938✔
125
        read_mem 5
3,938✔
126
        pop 1
3,938✔
127
        dup 5
3,938✔
128
        dup 13
3,938✔
129
        dup 12
3,938✔
130
        dup 12
3,938✔
131
        dup 12
3,938✔
132
        dup 12
3,938✔
133
        dup 12
3,938✔
134
        call tasmlib_hashing_merkle_verify
3,938✔
135
        call tasmlib_io_read_secin___bfe
3,938✔
136
        hint input_utxos_pointer = stack[0]
3,938✔
137

3,938✔
138
        /* Audit preloaded data */
3,938✔
139
        // ... *input_utxos_pointer
3,938✔
140
        dup 0
3,938✔
141
        call {audit_preloaded_data}
3,938✔
142
        // ... *input_utxos_pointer witness_size
3,938✔
143

3,938✔
144
        pop 1
3,938✔
145
        // ... *input_utxos_pointer
3,938✔
146

3,938✔
147
        call tasmlib_io_read_secin___bfe
3,938✔
148
        split
3,938✔
149
        hint _output_utxos_pointer = stack[0..2]
3,938✔
150
        dup 2
3,938✔
151
        hint input_salted_utxos = stack[0]
3,938✔
152
        dup 0
3,938✔
153
        call tasm_langs_hash_varlen_boxed_value___SaltedUtxos
3,938✔
154
        hint input_salted_utxos_digest = stack[0..5]
3,938✔
155
        dup 4
3,938✔
156
        dup 4
3,938✔
157
        dup 4
3,938✔
158
        dup 4
3,938✔
159
        dup 4
3,938✔
160
        push -12
3,938✔
161
        read_mem 5
3,938✔
162
        pop 1
3,938✔
163
        call tasmlib_hashing_eq_digest
3,938✔
164
        assert
3,938✔
165
        dup 5
3,938✔
166
        addi 4
3,938✔
167
        hint input_utxos = stack[0]
3,938✔
168
        push 0
3,938✔
169
        hint i = stack[0]
3,938✔
170
        call _binop_Lt__LboolR_bool_32_while_loop
3,938✔
171
        pop 5
3,938✔
172
        pop 5
3,938✔
173
        pop 5
3,938✔
174
        pop 5
3,938✔
175
        pop 5
3,938✔
176
        pop 5
3,938✔
177
        pop 5
3,938✔
178
        pop 4
3,938✔
179
        return
3,938✔
180
        _binop_Eq__LboolR_bool_49_then:
3,938✔
181
        pop 1
3,938✔
182
        dup 0
3,938✔
183
        addi 1
3,938✔
184
        hint state = stack[0]
3,938✔
185
        dup 0
3,938✔
186
        read_mem 1
3,938✔
187
        pop 1
3,938✔
188
        push 1
3,938✔
189
        eq
3,938✔
190
        assert
3,938✔
191
        dup 0
3,938✔
192
        push 0
3,938✔
193
        push 1
3,938✔
194
        mul
3,938✔
195
        push 1
3,938✔
196
        add
3,938✔
197
        push 00000000001073741824
3,938✔
198
        dup 1
3,938✔
199
        lt
3,938✔
200
        assert
3,938✔
201
        add
3,938✔
202
        read_mem 1
3,938✔
203
        pop 1
3,938✔
204
        hint release_date = stack[0]
3,938✔
205
        dup 0
3,938✔
206
        split
3,938✔
207
        push -17
3,938✔
208
        read_mem 1
3,938✔
209
        pop 1
3,938✔
210
        split
3,938✔
211
        swap 3
3,938✔
212
        swap 1
3,938✔
213
        swap 3
3,938✔
214
        swap 2
3,938✔
215
        call tasmlib_arithmetic_u64_lt
3,938✔
216
        assert
3,938✔
217
        pop 1
3,938✔
218
        pop 1
3,938✔
219
        push 0
3,938✔
220
        return
3,938✔
221
        _binop_Eq__LboolR_bool_49_else:
3,938✔
222
        return
3,938✔
223
        _binop_Lt__LboolR_bool_42_while_loop:
3,938✔
224
        dup 0
3,938✔
225
        dup 2
3,938✔
226
        read_mem 1
3,938✔
227
        pop 1
3,938✔
228
        swap 1
3,938✔
229
        lt
3,938✔
230
        push 0
3,938✔
231
        eq
3,938✔
232
        skiz
3,938✔
233
        return
3,938✔
234
        dup 1
3,938✔
235
        push 1
3,938✔
236
        add
3,938✔
237
        dup 1
3,938✔
238
        call tasm_langs_dynamic_list_element_finder
3,938✔
239
        pop 1
3,938✔
240
        addi 1
3,938✔
241
        hint coin = stack[0]
3,938✔
242
        dup 0
3,938✔
243
        read_mem 1
3,938✔
244
        push 00000000001073741824
3,938✔
245
        dup 2
3,938✔
246
        lt
3,938✔
247
        assert
3,938✔
248
        addi 2
3,938✔
249
        add
3,938✔
250
        push 4
3,938✔
251
        add
3,938✔
252
        read_mem 5
3,938✔
253
        pop 1
3,938✔
254
        push -2
3,938✔
255
        read_mem 5
3,938✔
256
        pop 1
3,938✔
257
        call tasmlib_hashing_eq_digest
3,938✔
258
        push 1
3,938✔
259
        swap 1
3,938✔
260
        skiz
3,938✔
261
        call _binop_Eq__LboolR_bool_49_then
3,938✔
262
        skiz
3,938✔
263
        call _binop_Eq__LboolR_bool_49_else
3,938✔
264
        dup 1
3,938✔
265
        push 1
3,938✔
266
        call tasmlib_arithmetic_u32_safeadd
3,938✔
267
        swap 2
3,938✔
268
        pop 1
3,938✔
269
        pop 1
3,938✔
270
        recurse
3,938✔
271
        _binop_Lt__LboolR_bool_32_while_loop:
3,938✔
272
        dup 0
3,938✔
273
        dup 2
3,938✔
274
        read_mem 1
3,938✔
275
        pop 1
3,938✔
276
        swap 1
3,938✔
277
        lt
3,938✔
278
        push 0
3,938✔
279
        eq
3,938✔
280
        skiz
3,938✔
281
        return
3,938✔
282
        dup 1
3,938✔
283
        push 1
3,938✔
284
        add
3,938✔
285
        dup 1
3,938✔
286
        call tasm_langs_dynamic_list_element_finder
3,938✔
287
        pop 1
3,938✔
288
        addi 1
3,938✔
289
        addi 1
3,938✔
290
        hint coins = stack[0]
3,938✔
291
        push 0
3,938✔
292
        hint j = stack[0]
3,938✔
293
        call _binop_Lt__LboolR_bool_42_while_loop
3,938✔
294
        dup 2
3,938✔
295
        push 1
3,938✔
296
        call tasmlib_arithmetic_u32_safeadd
3,938✔
297
        swap 3
3,938✔
298
        pop 1
3,938✔
299
        pop 1
3,938✔
300
        pop 1
3,938✔
301
        recurse
3,938✔
302
        encode_BField:
3,938✔
303
        call tasmlib_memory_dyn_malloc
3,938✔
304
        push 1
3,938✔
305
        swap 1
3,938✔
306
        write_mem 1
3,938✔
307
        write_mem 1
3,938✔
308
        push -2
3,938✔
309
        add
3,938✔
310
        return
3,938✔
311
        tasm_langs_dynamic_list_element_finder:
3,938✔
312
        dup 0
3,938✔
313
        push 0
3,938✔
314
        eq
3,938✔
315
        skiz
3,938✔
316
        return
3,938✔
317
        swap 1
3,938✔
318
        read_mem 1
3,938✔
319
        push 00000000001073741824
3,938✔
320
        dup 2
3,938✔
321
        lt
3,938✔
322
        assert
3,938✔
323
        addi 2
3,938✔
324
        add
3,938✔
325
        swap 1
3,938✔
326
        addi -1
3,938✔
327
        recurse
3,938✔
328
        tasm_langs_hash_varlen:
3,938✔
329
        read_mem 1
3,938✔
330
        push 2
3,938✔
331
        add
3,938✔
332
        swap 1
3,938✔
333
        call tasmlib_hashing_algebraic_hasher_hash_varlen
3,938✔
334
        return
3,938✔
335
        tasm_langs_hash_varlen_boxed_value___SaltedUtxos:
3,938✔
336
        dup 0
3,938✔
337
        push 0
3,938✔
338
        addi 3
3,938✔
339
        swap 1
3,938✔
340
        addi 3
3,938✔
341
        swap 1
3,938✔
342
        dup 1
3,938✔
343
        read_mem 1
3,938✔
344
        pop 1
3,938✔
345
        push 00000000001073741824
3,938✔
346
        dup 1
3,938✔
347
        lt
3,938✔
348
        assert
3,938✔
349
        addi 1
3,938✔
350
        dup 2
3,938✔
351
        dup 1
3,938✔
352
        add
3,938✔
353
        swap 3
3,938✔
354
        pop 1
3,938✔
355
        add
3,938✔
356
        swap 1
3,938✔
357
        pop 1
3,938✔
358
        call tasmlib_hashing_algebraic_hasher_hash_varlen
3,938✔
359
        return
3,938✔
360
        tasmlib_arithmetic_u32_safeadd:
3,938✔
361
        hint input_lhs: u32 = stack[0]
3,938✔
362
        hint input_rhs: u32 = stack[1]
3,938✔
363
        add
3,938✔
364
        dup 0
3,938✔
365
        split
3,938✔
366
        pop 1
3,938✔
367
        push 0
3,938✔
368
        eq
3,938✔
369
        assert
3,938✔
370
        return
3,938✔
371
        tasmlib_arithmetic_u64_lt:
3,938✔
372
        hint lhs: u64 = stack[0..2]
3,938✔
373
        hint rhs: u64 = stack[2..4]
3,938✔
374
        swap 3
3,938✔
375
        swap 2
3,938✔
376
        dup 2
3,938✔
377
        dup 2
3,938✔
378
        lt
3,938✔
379
        swap 4
3,938✔
380
        lt
3,938✔
381
        swap 2
3,938✔
382
        eq
3,938✔
383
        mul
3,938✔
384
        add
3,938✔
385
        return
3,938✔
386
        tasmlib_hashing_absorb_multiple:
3,938✔
387
        hint len: u32 = stack[0]
3,938✔
388
        hint _sequence: void_pointer = stack[1]
3,938✔
389
        dup 0
3,938✔
390
        push 10
3,938✔
391
        swap 1
3,938✔
392
        div_mod
3,938✔
393
        swap 1
3,938✔
394
        pop 1
3,938✔
395
        swap 1
3,938✔
396
        dup 1
3,938✔
397
        push -1
3,938✔
398
        mul
3,938✔
399
        dup 3
3,938✔
400
        add
3,938✔
401
        add
3,938✔
402
        swap 1
3,938✔
403
        swap 2
3,938✔
404
        push 0
3,938✔
405
        push 0
3,938✔
406
        push 0
3,938✔
407
        push 0
3,938✔
408
        swap 4
3,938✔
409
        call tasmlib_hashing_absorb_multiple_hash_all_full_chunks
3,938✔
410
        pop 5
3,938✔
411
        push -1
3,938✔
412
        add
3,938✔
413
        push 9
3,938✔
414
        dup 2
3,938✔
415
        push -1
3,938✔
416
        mul
3,938✔
417
        add
3,938✔
418
        call tasmlib_hashing_absorb_multiple_pad_varnum_zeros
3,938✔
419
        pop 1
3,938✔
420
        push 1
3,938✔
421
        swap 2
3,938✔
422
        dup 1
3,938✔
423
        add
3,938✔
424
        call tasmlib_hashing_absorb_multiple_read_remainder
3,938✔
425
        pop 2
3,938✔
426
        sponge_absorb
3,938✔
427
        return
3,938✔
428
        tasmlib_hashing_absorb_multiple_hash_all_full_chunks:
3,938✔
429
        dup 5
3,938✔
430
        dup 1
3,938✔
431
        eq
3,938✔
432
        skiz
3,938✔
433
        return
3,938✔
434
        sponge_absorb_mem
3,938✔
435
        recurse
3,938✔
436
        tasmlib_hashing_absorb_multiple_pad_varnum_zeros:
3,938✔
437
        dup 0
3,938✔
438
        push 0
3,938✔
439
        eq
3,938✔
440
        skiz
3,938✔
441
        return
3,938✔
442
        push 0
3,938✔
443
        swap 3
3,938✔
444
        swap 2
3,938✔
445
        swap 1
3,938✔
446
        push -1
3,938✔
447
        add
3,938✔
448
        recurse
3,938✔
449
        tasmlib_hashing_absorb_multiple_read_remainder:
3,938✔
450
        dup 1
3,938✔
451
        dup 1
3,938✔
452
        eq
3,938✔
453
        skiz
3,938✔
454
        return
3,938✔
455
        read_mem 1
3,938✔
456
        swap 1
3,938✔
457
        swap 2
3,938✔
458
        swap 1
3,938✔
459
        recurse
3,938✔
460
        tasmlib_hashing_algebraic_hasher_hash_varlen:
3,938✔
461
        hint length: u32 = stack[0]
3,938✔
462
        hint _addr: void_pointer = stack[1]
3,938✔
463
        sponge_init
3,938✔
464
        call tasmlib_hashing_absorb_multiple
3,938✔
465
        sponge_squeeze
3,938✔
466
        swap 5
3,938✔
467
        pop 1
3,938✔
468
        swap 5
3,938✔
469
        pop 1
3,938✔
470
        swap 5
3,938✔
471
        pop 1
3,938✔
472
        swap 5
3,938✔
473
        pop 1
3,938✔
474
        swap 5
3,938✔
475
        pop 1
3,938✔
476
        return
3,938✔
477
        tasmlib_hashing_eq_digest:
3,938✔
478
        hint input_a4: digest = stack[0..5]
3,938✔
479
        hint input_b4: digest = stack[5..10]
3,938✔
480
        swap 6
3,938✔
481
        eq
3,938✔
482
        swap 6
3,938✔
483
        eq
3,938✔
484
        swap 6
3,938✔
485
        eq
3,938✔
486
        swap 6
3,938✔
487
        eq
3,938✔
488
        swap 2
3,938✔
489
        eq
3,938✔
490
        mul
3,938✔
491
        mul
3,938✔
492
        mul
3,938✔
493
        mul
3,938✔
494
        return
3,938✔
495
        tasmlib_hashing_merkle_verify:
3,938✔
496
        hint leaf: digest = stack[0..5]
3,938✔
497
        hint leaf_index: u32 = stack[5]
3,938✔
498
        hint tree_height: u32 = stack[6]
3,938✔
499
        hint root: digest = stack[7..12]
3,938✔
500
        dup 6
3,938✔
501
        push 2
3,938✔
502
        pow
3,938✔
503
        dup 0
3,938✔
504
        dup 7
3,938✔
505
        lt
3,938✔
506
        assert
3,938✔
507
        dup 6
3,938✔
508
        add
3,938✔
509
        swap 6
3,938✔
510
        pop 1
3,938✔
511
        dup 6
3,938✔
512
        skiz
3,938✔
513
        call tasmlib_hashing_merkle_verify_tree_height_is_not_zero
3,938✔
514
        swap 2
3,938✔
515
        swap 4
3,938✔
516
        swap 6
3,938✔
517
        pop 1
3,938✔
518
        swap 2
3,938✔
519
        swap 4
3,938✔
520
        pop 1
3,938✔
521
        assert_vector
3,938✔
522
        pop 5
3,938✔
523
        return
3,938✔
524
        tasmlib_hashing_merkle_verify_tree_height_is_not_zero:
3,938✔
525
        push 1
3,938✔
526
        swap 7
3,938✔
527
        pop 1
3,938✔
528
        call tasmlib_hashing_merkle_verify_traverse_tree
3,938✔
529
        return
3,938✔
530
        tasmlib_hashing_merkle_verify_traverse_tree:
3,938✔
531
        merkle_step
3,938✔
532
        recurse_or_return
3,938✔
533
        tasmlib_io_read_secin___bfe:
3,938✔
534
        divine 1
3,938✔
535
        return
3,938✔
536
        tasmlib_io_read_stdin___digest:
3,938✔
537
        read_io 5
3,938✔
538
        return
3,938✔
539
        tasmlib_memory_dyn_malloc:
3,938✔
540
        push -1
3,938✔
541
        read_mem 1
3,938✔
542
        pop 1
3,938✔
543
        dup 0
3,938✔
544
        push 0
3,938✔
545
        eq
3,938✔
546
        skiz
3,938✔
547
        call tasmlib_memory_dyn_malloc_initialize
3,938✔
548
        push 00000000002147483647
3,938✔
549
        dup 1
3,938✔
550
        lt
3,938✔
551
        assert
3,938✔
552
        dup 0
3,938✔
553
        push 1
3,938✔
554
        add
3,938✔
555
        push -1
3,938✔
556
        write_mem 1
3,938✔
557
        pop 1
3,938✔
558
        push 00000000004294967296
3,938✔
559
        mul
3,938✔
560
        return
3,938✔
561
        tasmlib_memory_dyn_malloc_initialize:
3,938✔
562
        pop 1
3,938✔
563
        push 1
3,938✔
564
        return
3,938✔
565
        tasmlib_verifier_own_program_digest:
3,938✔
566
        dup 15
3,938✔
567
        dup 15
3,938✔
568
        dup 15
3,938✔
569
        dup 15
3,938✔
570
        dup 15
3,938✔
571
        return
3,938✔
572
        };
3,938✔
573

3,938✔
574
        (library, code)
3,938✔
575
    }
3,938✔
576

577
    fn hash(&self) -> Digest {
300,971✔
578
        static HASH: OnceLock<Digest> = OnceLock::new();
579

580
        *HASH.get_or_init(|| self.program().hash())
300,971✔
581
    }
300,971✔
582
}
583

584
impl TypeScript for TimeLock {
585
    type State = Timestamp;
586
}
587

588
#[derive(Debug, Clone, Deserialize, Serialize, BFieldCodec, GetSize, PartialEq, Eq)]
×
589
pub struct TimeLockWitness {
590
    /// One timestamp for every input UTXO. Inputs that do not have a time lock are
591
    /// assigned timestamp 0, which is automatically satisfied.
592
    release_dates: Vec<Timestamp>,
593
    input_utxos: SaltedUtxos,
594
    transaction_kernel: TransactionKernel,
595
}
596

597
type TimeLockWitnessMemory = SaltedUtxos;
598

599
impl SecretWitness for TimeLockWitness {
600
    fn nondeterminism(&self) -> NonDeterminism {
3,724✔
601
        let mut memory: HashMap<BFieldElement, BFieldElement> = HashMap::new();
3,724✔
602
        let input_salted_utxos_address = FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS;
3,724✔
603
        let output_salted_utxos_address = encode_to_memory::<TimeLockWitnessMemory>(
3,724✔
604
            &mut memory,
3,724✔
605
            input_salted_utxos_address,
3,724✔
606
            &self.input_utxos,
3,724✔
607
        );
3,724✔
608
        let individual_tokens = vec![
3,724✔
609
            self.transaction_kernel.timestamp.0,
3,724✔
610
            input_salted_utxos_address,
3,724✔
611
            output_salted_utxos_address,
3,724✔
612
        ];
3,724✔
613
        let mast_path = self
3,724✔
614
            .transaction_kernel
3,724✔
615
            .mast_path(TransactionKernelField::Timestamp)
3,724✔
616
            .clone();
3,724✔
617
        NonDeterminism::new(individual_tokens)
3,724✔
618
            .with_digests(mast_path)
3,724✔
619
            .with_ram(memory)
3,724✔
620
    }
3,724✔
621

622
    fn standard_input(&self) -> PublicInput {
162✔
623
        self.type_script_standard_input()
162✔
624
    }
162✔
625

626
    fn program(&self) -> Program {
×
627
        TimeLock.program()
×
628
    }
×
629
}
630

631
impl TypeScriptWitness for TimeLockWitness {
632
    fn transaction_kernel(&self) -> TransactionKernel {
162✔
633
        self.transaction_kernel.clone()
162✔
634
    }
162✔
635

636
    fn salted_input_utxos(&self) -> SaltedUtxos {
162✔
637
        self.input_utxos.clone()
162✔
638
    }
162✔
639

640
    fn salted_output_utxos(&self) -> SaltedUtxos {
162✔
641
        SaltedUtxos::empty()
162✔
642
    }
162✔
643

644
    fn type_script_and_witness(&self) -> TypeScriptAndWitness {
3,562✔
645
        TypeScriptAndWitness::new_with_nondeterminism(TimeLock.program(), self.nondeterminism())
3,562✔
646
    }
3,562✔
647

648
    fn new(
3,562✔
649
        transaction_kernel: TransactionKernel,
3,562✔
650
        salted_input_utxos: SaltedUtxos,
3,562✔
651
        _salted_output_utxos: SaltedUtxos,
3,562✔
652
    ) -> Self {
3,562✔
653
        let release_dates = salted_input_utxos
3,562✔
654
            .utxos
3,562✔
655
            .iter()
3,562✔
656
            .map(TimeLock::extract_release_date)
3,562✔
657
            .collect_vec();
3,562✔
658

3,562✔
659
        Self {
3,562✔
660
            release_dates,
3,562✔
661
            input_utxos: salted_input_utxos,
3,562✔
662
            transaction_kernel,
3,562✔
663
        }
3,562✔
664
    }
3,562✔
665
}
666

667
impl From<PrimitiveWitness> for TimeLockWitness {
668
    fn from(primitive_witness: PrimitiveWitness) -> Self {
81✔
669
        let release_dates = primitive_witness
81✔
670
            .input_utxos
81✔
671
            .utxos
81✔
672
            .iter()
81✔
673
            .map(TimeLock::extract_release_date)
81✔
674
            .collect_vec();
81✔
675
        let transaction_kernel = TransactionKernel::from(primitive_witness.clone());
81✔
676
        let input_utxos = primitive_witness.input_utxos.clone();
81✔
677

81✔
678
        Self {
81✔
679
            release_dates,
81✔
680
            input_utxos,
81✔
681
            transaction_kernel,
81✔
682
        }
81✔
683
    }
81✔
684
}
685

686
#[cfg(any(test, feature = "arbitrary-impls"))]
687
pub mod neptune_arbitrary {
688
    use num_traits::CheckedSub;
689
    use proptest::arbitrary::Arbitrary;
690
    use proptest::collection::vec;
691
    use proptest::strategy::BoxedStrategy;
692
    use proptest::strategy::Strategy;
693
    use proptest_arbitrary_interop::arb;
694

695
    use super::super::native_currency_amount::NativeCurrencyAmount;
696
    use super::*;
697
    use crate::models::blockchain::transaction::transaction_kernel::TransactionKernelModifier;
698
    use crate::models::blockchain::transaction::PublicAnnouncement;
699

700
    impl Arbitrary for TimeLockWitness {
701
        /// Parameters are:
702
        ///  - release_dates : `Vec<u64>` One release date per input UTXO. 0 if the time lock
703
        ///    coin is absent.
704
        ///  - num_outputs : usize Number of outputs.
705
        ///  - num_public_announcements : usize Number of public announcements.
706
        ///  - transaction_timestamp: Timestamp determining when the transaction takes place.
707
        type Parameters = (Vec<Timestamp>, usize, usize, Timestamp);
708

709
        type Strategy = BoxedStrategy<Self>;
710

711
        fn arbitrary_with(parameters: Self::Parameters) -> Self::Strategy {
61✔
712
            let (release_dates, num_outputs, num_public_announcements, transaction_timestamp) =
61✔
713
                parameters;
61✔
714
            let num_inputs = release_dates.len();
61✔
715
            (
61✔
716
                vec(arb::<Digest>(), num_inputs),
61✔
717
                vec(NativeCurrencyAmount::arbitrary_non_negative(), num_inputs),
61✔
718
                vec(arb::<Digest>(), num_outputs),
61✔
719
                vec(NativeCurrencyAmount::arbitrary_non_negative(), num_outputs),
61✔
720
                vec(arb::<PublicAnnouncement>(), num_public_announcements),
61✔
721
                NativeCurrencyAmount::arbitrary_coinbase(),
61✔
722
                NativeCurrencyAmount::arbitrary_non_negative(),
61✔
723
            )
61✔
724
                .prop_flat_map(
61✔
725
                    move |(
61✔
726
                        input_address_seeds,
727
                        input_amounts,
728
                        output_address_seeds,
729
                        mut output_amounts,
730
                        public_announcements,
731
                        maybe_coinbase,
732
                        mut fee,
733
                    )| {
61✔
734
                        // generate inputs
61✔
735
                        let (mut input_utxos, input_lock_scripts_and_witnesses) =
61✔
736
                            PrimitiveWitness::transaction_inputs_from_address_seeds_and_amounts(
61✔
737
                                &input_address_seeds,
61✔
738
                                &input_amounts,
61✔
739
                            );
61✔
740
                        let total_inputs = input_amounts.into_iter().sum::<NativeCurrencyAmount>();
61✔
741

742
                        // add time locks to input UTXOs (changes Utxo hash)
743
                        for (utxo, release_date) in input_utxos.iter_mut().zip(release_dates.iter())
129✔
744
                        {
745
                            if !release_date.is_zero() {
129✔
746
                                let time_lock_coin = TimeLock::until(*release_date);
83✔
747
                                let mut coins = utxo.coins().to_vec();
83✔
748
                                coins.push(time_lock_coin);
83✔
749
                                *utxo = (utxo.lock_script_hash(), coins).into()
83✔
750
                            }
46✔
751
                        }
752

753
                        // generate valid output amounts
754
                        PrimitiveWitness::find_balanced_output_amounts_and_fee(
61✔
755
                            total_inputs,
61✔
756
                            maybe_coinbase,
61✔
757
                            &mut output_amounts,
61✔
758
                            &mut fee,
61✔
759
                        );
61✔
760

61✔
761
                        // generate output UTXOs
61✔
762
                        let output_utxos =
61✔
763
                            PrimitiveWitness::valid_tx_outputs_from_amounts_and_address_seeds(
61✔
764
                                &output_amounts,
61✔
765
                                &output_address_seeds,
61✔
766
                                None,
61✔
767
                            );
61✔
768

61✔
769
                        // generate primitive transaction witness and time lock witness from there
61✔
770
                        PrimitiveWitness::arbitrary_primitive_witness_with(
61✔
771
                            &input_utxos,
61✔
772
                            &input_lock_scripts_and_witnesses,
61✔
773
                            &output_utxos,
61✔
774
                            &public_announcements,
61✔
775
                            NativeCurrencyAmount::zero(),
61✔
776
                            maybe_coinbase,
61✔
777
                        )
61✔
778
                        .prop_map(move |mut transaction_primitive_witness| {
61✔
779
                            let modified_kernel = TransactionKernelModifier::default()
61✔
780
                                .timestamp(transaction_timestamp)
61✔
781
                                .modify(transaction_primitive_witness.kernel);
61✔
782

61✔
783
                            transaction_primitive_witness.kernel = modified_kernel;
61✔
784
                            TimeLockWitness::from(transaction_primitive_witness)
61✔
785
                        })
61✔
786
                        .boxed()
61✔
787
                    },
61✔
788
                )
61✔
789
                .boxed()
61✔
790
        }
61✔
791
    }
792

793
    /// Generate a `Strategy` for a [`PrimitiveWitness`] with the given numbers of
794
    /// inputs, outputs, and public announcements, with active timelocks.
795
    ///
796
    /// The UTXOs are timelocked with a release date set between `now` and six
797
    /// months from `now`.
798
    ///
799
    // Public bc used in benchmarks.
800
    #[doc(hidden)]
801
    pub fn arbitrary_primitive_witness_with_active_timelocks(
75✔
802
        num_inputs: usize,
75✔
803
        num_outputs: usize,
75✔
804
        num_announcements: usize,
75✔
805
        now: Timestamp,
75✔
806
    ) -> BoxedStrategy<PrimitiveWitness> {
75✔
807
        vec(
75✔
808
            Timestamp::arbitrary_between(now, now + Timestamp::months(6)),
75✔
809
            num_inputs + num_outputs,
75✔
810
        )
75✔
811
        .prop_flat_map(move |release_dates| {
75✔
812
            arbitrary_primitive_witness_with_timelocks(
75✔
813
                num_inputs,
75✔
814
                num_outputs,
75✔
815
                num_announcements,
75✔
816
                now,
75✔
817
                release_dates,
75✔
818
            )
75✔
819
        })
75✔
820
        .boxed()
75✔
821
    }
75✔
822

823
    /// Generate a `Strategy` for a [`PrimitiveWitness`] with the given numbers of
824
    /// inputs, outputs, and public announcements, with expired timelocks.
825
    ///
826
    /// The UTXOs are timelocked with a release date set between six months in the
827
    /// past relative to `now` and `now`.
828
    ///
829
    // Public bc used in benchmarks.
830
    #[doc(hidden)]
831
    pub fn arbitrary_primitive_witness_with_expired_timelocks(
33✔
832
        num_inputs: usize,
33✔
833
        num_outputs: usize,
33✔
834
        num_announcements: usize,
33✔
835
        now: Timestamp,
33✔
836
    ) -> BoxedStrategy<PrimitiveWitness> {
33✔
837
        vec(
33✔
838
            Timestamp::arbitrary_between(now - Timestamp::months(6), now - Timestamp::millis(1)),
33✔
839
            num_inputs + num_outputs,
33✔
840
        )
33✔
841
        .prop_flat_map(move |release_dates| {
137✔
842
            arbitrary_primitive_witness_with_timelocks(
137✔
843
                num_inputs,
137✔
844
                num_outputs,
137✔
845
                num_announcements,
137✔
846
                now,
137✔
847
                release_dates,
137✔
848
            )
137✔
849
        })
137✔
850
        .boxed()
33✔
851
    }
33✔
852

853
    #[expect(unused_variables, reason = "under development")]
854
    fn arbitrary_primitive_witness_with_timelocks(
212✔
855
        num_inputs: usize,
212✔
856
        num_outputs: usize,
212✔
857
        num_announcements: usize,
212✔
858
        now: Timestamp,
212✔
859
        release_dates: Vec<Timestamp>,
212✔
860
    ) -> BoxedStrategy<PrimitiveWitness> {
212✔
861
        (
212✔
862
            NativeCurrencyAmount::arbitrary_non_negative(),
212✔
863
            vec(arb::<Digest>(), num_inputs),
212✔
864
            vec(arb::<u64>(), num_inputs),
212✔
865
            vec(arb::<Digest>(), num_outputs),
212✔
866
            vec(arb::<u64>(), num_outputs),
212✔
867
            vec(arb::<PublicAnnouncement>(), num_announcements),
212✔
868
            arb::<u64>(),
212✔
869
            arb::<Option<u64>>(),
212✔
870
        )
212✔
871
            .prop_flat_map(
212✔
872
                move |(
212✔
873
                    total_amount,
874
                    input_address_seeds,
875
                    input_dist,
876
                    output_address_seeds,
877
                    output_dist,
878
                    public_announcements,
879
                    fee_dist,
880
                    maybe_coinbase_dist,
881
                )| {
212✔
882
                    let maybe_coinbase_dist = if num_inputs.is_zero() {
212✔
883
                        maybe_coinbase_dist
1✔
884
                    } else {
885
                        None
211✔
886
                    };
887

888
                    // distribute total amount across inputs (+ coinbase)
889
                    let mut input_denominator = input_dist.iter().map(|u| *u as f64).sum::<f64>();
425✔
890
                    if let Some(d) = maybe_coinbase_dist {
212✔
891
                        input_denominator += d as f64;
×
892
                    }
212✔
893
                    let input_weights = input_dist
212✔
894
                        .into_iter()
212✔
895
                        .map(|u| (u as f64) / input_denominator)
425✔
896
                        .collect_vec();
212✔
897
                    let mut input_amounts = input_weights
212✔
898
                        .into_iter()
212✔
899
                        .map(|w| total_amount.to_nau_f64() * w)
425✔
900
                        .map(|f| NativeCurrencyAmount::try_from(f).unwrap())
425✔
901
                        .collect_vec();
212✔
902
                    let maybe_coinbase = if maybe_coinbase_dist.is_some()
212✔
903
                        || input_amounts.is_empty()
212✔
904
                    {
905
                        Some(
1✔
906
                            total_amount
1✔
907
                                .checked_sub(
1✔
908
                                    &input_amounts.iter().copied().sum::<NativeCurrencyAmount>(),
1✔
909
                                )
1✔
910
                                .unwrap(),
1✔
911
                        )
1✔
912
                    } else {
913
                        let sum_of_all_but_last = input_amounts
211✔
914
                            .iter()
211✔
915
                            .rev()
211✔
916
                            .skip(1)
211✔
917
                            .copied()
211✔
918
                            .sum::<NativeCurrencyAmount>();
211✔
919
                        *input_amounts.last_mut().unwrap() =
211✔
920
                            total_amount.checked_sub(&sum_of_all_but_last).unwrap();
211✔
921
                        None
211✔
922
                    };
923

924
                    // distribute total amount across outputs
925
                    let output_denominator =
212✔
926
                        output_dist.iter().map(|u| *u as f64).sum::<f64>() + (fee_dist as f64);
390✔
927
                    let output_weights = output_dist
212✔
928
                        .into_iter()
212✔
929
                        .map(|u| (u as f64) / output_denominator)
390✔
930
                        .collect_vec();
212✔
931
                    let output_amounts = output_weights
212✔
932
                        .into_iter()
212✔
933
                        .map(|w| total_amount.to_nau_f64() * w)
390✔
934
                        .map(|f| NativeCurrencyAmount::try_from(f).unwrap())
390✔
935
                        .collect_vec();
212✔
936
                    let total_outputs =
212✔
937
                        output_amounts.iter().copied().sum::<NativeCurrencyAmount>();
212✔
938
                    let fee = total_amount.checked_sub(&total_outputs).unwrap();
212✔
939

212✔
940
                    let (mut input_utxos, input_lock_scripts_and_witnesses) =
212✔
941
                        PrimitiveWitness::transaction_inputs_from_address_seeds_and_amounts(
212✔
942
                            &input_address_seeds,
212✔
943
                            &input_amounts,
212✔
944
                        );
212✔
945
                    let total_inputs = input_amounts.iter().copied().sum::<NativeCurrencyAmount>();
212✔
946

212✔
947
                    assert_eq!(
212✔
948
                        total_inputs + maybe_coinbase.unwrap_or(NativeCurrencyAmount::coins(0)),
212✔
949
                        total_outputs + fee
212✔
950
                    );
212✔
951
                    let mut output_utxos =
212✔
952
                        PrimitiveWitness::valid_tx_outputs_from_amounts_and_address_seeds(
212✔
953
                            &output_amounts,
212✔
954
                            &output_address_seeds,
212✔
955
                            None,
212✔
956
                        );
212✔
957
                    let mut counter = 0usize;
212✔
958
                    for utxo in &mut input_utxos {
637✔
959
                        let release_date = release_dates[counter];
425✔
960
                        let time_lock = TimeLock::until(release_date);
425✔
961
                        let mut coins = utxo.coins().to_vec();
425✔
962
                        coins.push(time_lock);
425✔
963
                        *utxo = Utxo::from((utxo.lock_script_hash(), coins));
425✔
964
                        counter += 1;
425✔
965
                    }
425✔
966
                    for utxo in &mut output_utxos {
602✔
967
                        let mut coins = utxo.coins().to_vec();
390✔
968
                        coins.push(TimeLock::until(release_dates[counter]));
390✔
969
                        *utxo = Utxo::from((utxo.lock_script_hash(), coins));
390✔
970
                        counter += 1;
390✔
971
                    }
390✔
972
                    let release_dates = release_dates.clone();
212✔
973

212✔
974
                    let merge_bit = false;
212✔
975
                    PrimitiveWitness::arbitrary_primitive_witness_with_timestamp_and(
212✔
976
                        &input_utxos,
212✔
977
                        &input_lock_scripts_and_witnesses,
212✔
978
                        &output_utxos,
212✔
979
                        &public_announcements,
212✔
980
                        fee,
212✔
981
                        maybe_coinbase,
212✔
982
                        now,
212✔
983
                        merge_bit,
212✔
984
                    )
212✔
985
                    .prop_map(move |primitive_witness_template| {
212✔
986
                        let mut primitive_witness = primitive_witness_template.clone();
212✔
987
                        let modified_kernel = TransactionKernelModifier::default()
212✔
988
                            .timestamp(now)
212✔
989
                            .modify(primitive_witness.kernel);
212✔
990

212✔
991
                        primitive_witness.kernel = modified_kernel;
212✔
992
                        primitive_witness
212✔
993
                    })
212✔
994
                },
212✔
995
            )
212✔
996
            .boxed()
212✔
997
    }
212✔
998
}
999

1000
#[cfg(test)]
1001
mod test {
1002
    use proptest::collection::vec;
1003
    use proptest::prelude::Arbitrary;
1004
    use proptest::prelude::Strategy;
1005
    use proptest::prop_assert;
1006
    use proptest::prop_assert_eq;
1007
    use proptest::strategy::Just;
1008
    use proptest::test_runner::TestRunner;
1009
    use proptest_arbitrary_interop::arb;
1010
    use tasm_lib::twenty_first::math::tip5::Tip5;
1011
    use test_strategy::proptest;
1012
    use tokio::runtime::Runtime;
1013

1014
    use super::neptune_arbitrary::arbitrary_primitive_witness_with_active_timelocks;
1015
    use super::neptune_arbitrary::arbitrary_primitive_witness_with_expired_timelocks;
1016
    use super::*;
1017
    use crate::models::proof_abstractions::tasm::builtins as tasm;
1018
    use crate::models::proof_abstractions::tasm::program::test::ConsensusProgramSpecification;
1019

1020
    impl ConsensusProgramSpecification for TimeLock {
1021
        #[expect(clippy::needless_return)]
1022
        fn source(&self) {
81✔
1023
            // get in the current program's hash digest
81✔
1024
            let self_digest: Digest = tasm::own_program_digest();
81✔
1025

81✔
1026
            // read standard input:
81✔
1027
            //  - transaction kernel mast hash
81✔
1028
            //  - input salted utxos digest
81✔
1029
            //  - output salted utxos digest
81✔
1030
            // (All type scripts take this triple as input.)
81✔
1031
            let tx_kernel_digest: Digest = tasm::tasmlib_io_read_stdin___digest();
81✔
1032
            let input_utxos_digest: Digest = tasm::tasmlib_io_read_stdin___digest();
81✔
1033
            let _output_utxos_digest: Digest = tasm::tasmlib_io_read_stdin___digest();
81✔
1034

81✔
1035
            // divine the timestamp and authenticate it against the kernel mast hash
81✔
1036
            let leaf_index: u32 = 5;
81✔
1037
            let timestamp: BFieldElement = tasm::tasmlib_io_read_secin___bfe();
81✔
1038
            let leaf: Digest = Tip5::hash_varlen(&timestamp.encode());
81✔
1039
            let tree_height: u32 = 3;
81✔
1040
            tasm::tasmlib_hashing_merkle_verify(tx_kernel_digest, leaf_index, leaf, tree_height);
81✔
1041

81✔
1042
            // get pointers to objects living in nondeterministic memory:
81✔
1043
            //  - input Salted UTXOs
81✔
1044
            let input_utxos_pointer: u64 = tasm::tasmlib_io_read_secin___bfe().value();
81✔
1045

81✔
1046
            // it's important to read the outputs digest too, but we actually don't care about
81✔
1047
            // the output UTXOs (in this type script)
81✔
1048
            let _output_utxos_pointer: u64 = tasm::tasmlib_io_read_secin___bfe().value();
81✔
1049

81✔
1050
            // authenticate salted input UTXOs against the digest that was read from stdin
81✔
1051
            let input_salted_utxos: SaltedUtxos =
81✔
1052
                tasm::decode_from_memory(BFieldElement::new(input_utxos_pointer));
81✔
1053
            let input_salted_utxos_digest: Digest = Tip5::hash(&input_salted_utxos);
81✔
1054
            assert_eq!(input_salted_utxos_digest, input_utxos_digest);
81✔
1055

1056
            // iterate over inputs
1057
            let input_utxos = input_salted_utxos.utxos;
81✔
1058
            let mut i = 0;
81✔
1059
            while i < input_utxos.len() {
195✔
1060
                // get coins
1061
                let coins = input_utxos[i].coins();
145✔
1062

145✔
1063
                // if this typescript is present
145✔
1064
                let mut j: usize = 0;
145✔
1065
                while j < coins.len() {
358✔
1066
                    let coin: &Coin = &coins[j];
244✔
1067
                    if coin.type_script_hash == self_digest {
244✔
1068
                        // extract state
1069
                        let state: &Vec<BFieldElement> = &coin.state;
99✔
1070

99✔
1071
                        // assert format
99✔
1072
                        assert!(state.len() == 1);
99✔
1073

1074
                        // extract timestamp
1075
                        let release_date: BFieldElement = state[0];
99✔
1076

99✔
1077
                        // test time lock
99✔
1078
                        assert!(release_date.value() < timestamp.value());
99✔
1079
                    }
145✔
1080
                    j += 1;
213✔
1081
                }
1082
                i += 1;
114✔
1083
            }
1084

1085
            return;
50✔
1086
        }
50✔
1087
    }
1088

1089
    #[proptest(cases = 20)]
100✔
1090
    fn test_unlocked(
1091
        #[strategy(1usize..=3)] _num_inputs: usize,
1✔
1092
        #[strategy(1usize..=3)] _num_outputs: usize,
1✔
1093
        #[strategy(1usize..=3)] _num_public_announcements: usize,
1✔
1094
        #[strategy(vec(Just(Timestamp::zero()), #_num_inputs))] _release_dates: Vec<Timestamp>,
20✔
1095
        #[strategy(Just::<Timestamp>(#_release_dates.iter().copied().min().unwrap()))]
1096
        _transaction_timestamp: Timestamp,
20✔
1097
        #[strategy(
1098
            TimeLockWitness::arbitrary_with((
1099
                #_release_dates,
1100
                #_num_outputs,
1101
                #_num_public_announcements,
1102
                #_transaction_timestamp,
1103
            ))
1104
        )]
1105
        time_lock_witness: TimeLockWitness,
20✔
1106
    ) {
1107
        let rust_result = TimeLock.run_rust(
1108
            &time_lock_witness.standard_input(),
1109
            time_lock_witness.nondeterminism(),
1110
        );
1111
        prop_assert!(
1112
            rust_result.is_ok(),
1113
            "time lock program did not halt gracefully"
1114
        );
1115
        let tasm_result = TimeLock.run_tasm(
1116
            &time_lock_witness.standard_input(),
1117
            time_lock_witness.nondeterminism(),
1118
        );
1119
        prop_assert!(
1120
            tasm_result.is_ok(),
1121
            "time lock program did not halt gracefully"
1122
        );
1123
        prop_assert_eq!(rust_result.unwrap(), tasm_result.unwrap());
1124
    }
1125

1126
    #[test]
1127
    fn tx_timestamp_same_as_release_time_must_fail() {
1✔
1128
        // Verify use of `>`, not `>=`.
1✔
1129
        let release_date = Timestamp::now();
1✔
1130
        let mut test_runner = TestRunner::deterministic();
1✔
1131
        let time_lock_witness =
1✔
1132
            TimeLockWitness::arbitrary_with((vec![release_date], 1, 0, release_date))
1✔
1133
                .new_tree(&mut test_runner)
1✔
1134
                .unwrap()
1✔
1135
                .current();
1✔
1136
        assert!(
1✔
1137
            TimeLock {}
1✔
1138
                .run_rust(
1✔
1139
                    &time_lock_witness.standard_input(),
1✔
1140
                    time_lock_witness.nondeterminism(),
1✔
1141
                )
1✔
1142
                .is_err(),
1✔
1143
            "time lock program failed to panic"
×
1144
        );
1145
        assert!(
1✔
1146
            TimeLock {}
1✔
1147
                .run_tasm(
1✔
1148
                    &time_lock_witness.standard_input(),
1✔
1149
                    time_lock_witness.nondeterminism(),
1✔
1150
                )
1✔
1151
                .is_err(),
1✔
1152
            "time lock program failed to panic"
×
1153
        );
1154
    }
1✔
1155

1156
    #[proptest(cases = 20)]
100✔
1157
    fn test_locked(
1158
        #[strategy(1usize..=3)] _num_inputs: usize,
1✔
1159
        #[strategy(1usize..=3)] _num_outputs: usize,
1✔
1160
        #[strategy(1usize..=3)] _num_public_announcements: usize,
1✔
1161
        #[strategy(
1162
            vec(
1163
                Timestamp::arbitrary_between(
1164
                    Timestamp::now() - Timestamp::days(7),
1165
                    Timestamp::now() - Timestamp::days(1),
1166
                ),
1167
                #_num_inputs,
1168
            )
1169
        )]
1170
        _release_dates: Vec<Timestamp>,
20✔
1171
        #[strategy(Just::<Timestamp>(#_release_dates.iter().copied().max().unwrap()))]
1172
        _tx_timestamp: Timestamp,
20✔
1173
        #[strategy(
1174
            TimeLockWitness::arbitrary_with((
1175
                #_release_dates,
1176
                #_num_outputs,
1177
                #_num_public_announcements,
1178
                #_tx_timestamp,
1179
            ))
1180
        )]
1181
        time_lock_witness: TimeLockWitness,
20✔
1182
    ) {
1183
        println!("now: {}", Timestamp::now());
1184
        prop_assert!(
1185
            TimeLock {}
1186
                .run_rust(
1187
                    &time_lock_witness.standard_input(),
1188
                    time_lock_witness.nondeterminism(),
1189
                )
1190
                .is_err(),
1191
            "time lock program failed to panic"
1192
        );
1193
        prop_assert!(
1194
            TimeLock {}
1195
                .run_tasm(
1196
                    &time_lock_witness.standard_input(),
1197
                    time_lock_witness.nondeterminism(),
1198
                )
1199
                .is_err(),
1200
            "time lock program failed to panic"
1201
        );
1202
    }
1203

1204
    #[proptest(cases = 20)]
100✔
1205
    fn test_released(
1206
        #[strategy(1usize..=3)] _num_inputs: usize,
1✔
1207
        #[strategy(1usize..=3)] _num_outputs: usize,
1✔
1208
        #[strategy(1usize..=3)] _num_public_announcements: usize,
1✔
1209
        #[strategy(
1210
            vec(
1211
                Timestamp::arbitrary_between(
1212
                    Timestamp::now() - Timestamp::days(7),
1213
                    Timestamp::now() - Timestamp::days(1),
1214
                ),
1215
                #_num_inputs,
1216
            )
1217
        )]
1218
        _release_dates: Vec<Timestamp>,
20✔
1219
        #[strategy(Just::<Timestamp>(#_release_dates.iter().copied().max().unwrap()))]
1220
        _tx_timestamp: Timestamp,
20✔
1221
        #[strategy(
1222
            TimeLockWitness::arbitrary_with((
1223
                #_release_dates,
1224
                #_num_outputs,
1225
                #_num_public_announcements,
1226
                #_tx_timestamp + Timestamp::days(1),
1227
            ))
1228
        )]
1229
        time_lock_witness: TimeLockWitness,
20✔
1230
    ) {
1231
        println!("now: {}", Timestamp::now());
1232
        let rust_result = TimeLock.run_rust(
1233
            &time_lock_witness.standard_input(),
1234
            time_lock_witness.nondeterminism(),
1235
        );
1236
        prop_assert!(
1237
            rust_result.is_ok(),
1238
            "time lock program did not halt gracefully"
1239
        );
1240
        let tasm_result = TimeLock.run_tasm(
1241
            &time_lock_witness.standard_input(),
1242
            time_lock_witness.nondeterminism(),
1243
        );
1244
        prop_assert!(
1245
            tasm_result.is_ok(),
1246
            "time lock program did not halt gracefully"
1247
        );
1248
        prop_assert_eq!(rust_result.unwrap(), tasm_result.unwrap());
1249
    }
1250

1251
    #[proptest(cases = 5)]
15✔
1252
    fn primitive_witness_with_active_timelocks_is_invalid(
1253
        #[strategy(arb::<Timestamp>())] _now: Timestamp,
1✔
1254
        #[strategy(arbitrary_primitive_witness_with_active_timelocks(2, 2, 2, #_now))]
1255
        primitive_witness: PrimitiveWitness,
5✔
1256
    ) {
1257
        // Negative test: Primitive witness spending inputs that are timelocked
1258
        // must fail to validate.
1259
        prop_assert!(!Runtime::new()
1260
            .unwrap()
1261
            .block_on(primitive_witness.validate()));
1262
    }
1263

1264
    #[proptest(cases = 10)]
30✔
1265
    fn arbitrary_primitive_witness_with_active_timelocks_fails(
1266
        #[strategy(arb::<Timestamp>())] _now: Timestamp,
1✔
1267
        #[strategy(arbitrary_primitive_witness_with_active_timelocks(2, 2, 2, #_now))]
1268
        primitive_witness: PrimitiveWitness,
10✔
1269
    ) {
1270
        let time_lock_witness = TimeLockWitness::from(primitive_witness);
1271

1272
        prop_assert!(
1273
            TimeLock {}
1274
                .run_rust(
1275
                    &time_lock_witness.standard_input(),
1276
                    time_lock_witness.nondeterminism(),
1277
                )
1278
                .is_err(),
1279
            "time lock program failed to panic"
1280
        );
1281
        prop_assert!(
1282
            TimeLock {}
1283
                .run_tasm(
1284
                    &time_lock_witness.standard_input(),
1285
                    time_lock_witness.nondeterminism(),
1286
                )
1287
                .is_err(),
1288
            "time lock program failed to panic"
1289
        );
1290
    }
1291

1292
    #[proptest(cases = 10)]
30✔
1293
    fn arbitrary_primitive_witness_with_expired_timelocks_passes(
1294
        #[strategy(arb::<Timestamp>())] _now: Timestamp,
1✔
1295
        #[strategy(arbitrary_primitive_witness_with_expired_timelocks(2, 2, 2, #_now))]
1296
        primitive_witness: PrimitiveWitness,
10✔
1297
    ) {
1298
        let time_lock_witness = TimeLockWitness::from(primitive_witness);
1299

1300
        let rust_result = TimeLock.run_rust(
1301
            &time_lock_witness.standard_input(),
1302
            time_lock_witness.nondeterminism(),
1303
        );
1304
        prop_assert!(
1305
            rust_result.is_ok(),
1306
            "time lock program did not halt gracefully"
1307
        );
1308
        let tasm_result = TimeLock.run_tasm(
1309
            &time_lock_witness.standard_input(),
1310
            time_lock_witness.nondeterminism(),
1311
        );
1312
        prop_assert!(
1313
            tasm_result.is_ok(),
1314
            "time lock program did not halt gracefully"
1315
        );
1316
        prop_assert_eq!(tasm_result.unwrap(), rust_result.unwrap());
1317
    }
1318
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc