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

Neptune-Crypto / neptune-core / 13934460366

18 Mar 2025 10:10PM UTC coverage: 84.31% (+0.03%) from 84.279%
13934460366

Pull #512

github

web-flow
Merge 9c4947ba6 into ee3795966
Pull Request #512: `proptest_state_machine` over `PeerMessage`

9 of 26 new or added lines in 7 files covered. (34.62%)

42 existing lines in 5 files now uncovered.

50796 of 60249 relevant lines covered (84.31%)

178560.53 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 {
274,760✔
40
        Coin {
274,760✔
41
            type_script_hash: TimeLock.hash(),
274,760✔
42
            state: vec![date.0],
274,760✔
43
        }
274,760✔
44
    }
274,760✔
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,690✔
49
        utxo.coins()
1,690✔
50
            .iter()
1,690✔
51
            .find_map(Coin::release_date)
1,690✔
52
            .unwrap_or_else(Timestamp::zero)
1,690✔
53
    }
1,690✔
54
}
55

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

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

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

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

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

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

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

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

580
        *HASH.get_or_init(|| self.program().hash())
303,008✔
581
    }
303,008✔
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,703✔
601
        let mut memory: HashMap<BFieldElement, BFieldElement> = HashMap::new();
3,703✔
602
        let input_salted_utxos_address = FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS;
3,703✔
603
        let output_salted_utxos_address = encode_to_memory::<TimeLockWitnessMemory>(
3,703✔
604
            &mut memory,
3,703✔
605
            input_salted_utxos_address,
3,703✔
606
            &self.input_utxos,
3,703✔
607
        );
3,703✔
608
        let individual_tokens = vec![
3,703✔
609
            self.transaction_kernel.timestamp.0,
3,703✔
610
            input_salted_utxos_address,
3,703✔
611
            output_salted_utxos_address,
3,703✔
612
        ];
3,703✔
613
        let mast_path = self
3,703✔
614
            .transaction_kernel
3,703✔
615
            .mast_path(TransactionKernelField::Timestamp)
3,703✔
616
            .clone();
3,703✔
617
        NonDeterminism::new(individual_tokens)
3,703✔
618
            .with_digests(mast_path)
3,703✔
619
            .with_ram(memory)
3,703✔
620
    }
3,703✔
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,541✔
645
        TypeScriptAndWitness::new_with_nondeterminism(TimeLock.program(), self.nondeterminism())
3,541✔
646
    }
3,541✔
647

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

3,541✔
659
        Self {
3,541✔
660
            release_dates,
3,541✔
661
            input_utxos: salted_input_utxos,
3,541✔
662
            transaction_kernel,
3,541✔
663
        }
3,541✔
664
    }
3,541✔
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())
123✔
744
                        {
745
                            if !release_date.is_zero() {
123✔
746
                                let time_lock_coin = TimeLock::until(*release_date);
79✔
747
                                let mut coins = utxo.coins().to_vec();
79✔
748
                                coins.push(time_lock_coin);
79✔
749
                                *utxo = (utxo.lock_script_hash(), coins).into()
79✔
750
                            }
44✔
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(
27✔
832
        num_inputs: usize,
27✔
833
        num_outputs: usize,
27✔
834
        num_announcements: usize,
27✔
835
        now: Timestamp,
27✔
836
    ) -> BoxedStrategy<PrimitiveWitness> {
27✔
837
        vec(
27✔
838
            Timestamp::arbitrary_between(now - Timestamp::months(6), now - Timestamp::millis(1)),
27✔
839
            num_inputs + num_outputs,
27✔
840
        )
27✔
841
        .prop_flat_map(move |release_dates| {
118✔
842
            arbitrary_primitive_witness_with_timelocks(
118✔
843
                num_inputs,
118✔
844
                num_outputs,
118✔
845
                num_announcements,
118✔
846
                now,
118✔
847
                release_dates,
118✔
848
            )
118✔
849
        })
118✔
850
        .boxed()
27✔
851
    }
27✔
852

853
    #[expect(unused_variables, reason = "under development")]
854
    fn arbitrary_primitive_witness_with_timelocks(
193✔
855
        num_inputs: usize,
193✔
856
        num_outputs: usize,
193✔
857
        num_announcements: usize,
193✔
858
        now: Timestamp,
193✔
859
        release_dates: Vec<Timestamp>,
193✔
860
    ) -> BoxedStrategy<PrimitiveWitness> {
193✔
861
        (
193✔
862
            NativeCurrencyAmount::arbitrary_non_negative(),
193✔
863
            vec(arb::<Digest>(), num_inputs),
193✔
864
            vec(arb::<u64>(), num_inputs),
193✔
865
            vec(arb::<Digest>(), num_outputs),
193✔
866
            vec(arb::<u64>(), num_outputs),
193✔
867
            vec(arb::<PublicAnnouncement>(), num_announcements),
193✔
868
            arb::<u64>(),
193✔
869
            arb::<Option<u64>>(),
193✔
870
        )
193✔
871
            .prop_flat_map(
193✔
872
                move |(
193✔
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
                )| {
193✔
882
                    let maybe_coinbase_dist = if num_inputs.is_zero() {
193✔
883
                        maybe_coinbase_dist
2✔
884
                    } else {
885
                        None
191✔
886
                    };
887

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

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

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

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

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

193✔
991
                        primitive_witness.kernel = modified_kernel;
193✔
992
                        primitive_witness
193✔
993
                    })
193✔
994
                },
193✔
995
            )
193✔
996
            .boxed()
193✔
997
    }
193✔
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() {
193✔
1060
                // get coins
1061
                let coins = input_utxos[i].coins();
143✔
1062

143✔
1063
                // if this typescript is present
143✔
1064
                let mut j: usize = 0;
143✔
1065
                while j < coins.len() {
354✔
1066
                    let coin: &Coin = &coins[j];
242✔
1067
                    if coin.type_script_hash == self_digest {
242✔
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
                    }
143✔
1080
                    j += 1;
211✔
1081
                }
1082
                i += 1;
112✔
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