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

Neptune-Crypto / neptune-core / 15714233880

17 Jun 2025 05:33PM UTC coverage: 71.774% (-0.02%) from 71.792%
15714233880

push

github

Sword-Smith
refactor: Don't reset difficulty on `Testnet` network

This restores `Testnet` behavior to that prior to
<a class=hub.com/Neptune-Crypto/neptune-core/commit/<a class="double-link" href="https://git"><a class=hub.com/Neptune-Crypto/neptune-core/commit/1293adaff5df7be02382b867424fca2604692eac">1293adaff. Except that the initial test
net difficulty is now 10^6 and not 10^9. So this commit is both a
hardfork in relation to behavior prior to this commit, and a hardfork
prior to 1293adaff5df7be02382b867424fca2604692eac. In other words: This
commit introduces testnet v2 that we can hopefully get up and running
again.

The few people who were running test net prior to
1293adaff5df7be02382b867424fca2604692eac will have to clear their
testnet data directory which is `~/.local/share/neptune/testnet`
on Linux machines.

1 of 1 new or added line in 1 file covered. (100.0%)

9 existing lines in 1 file now uncovered.

20109 of 28017 relevant lines covered (71.77%)

487153.16 hits per line

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

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

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

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

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

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

144
        pop 1
145
        // ... *input_utxos_pointer
146

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

574
        (library, code)
4,025✔
575
    }
4,025✔
576

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

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

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

659
        Self {
3,620✔
660
            release_dates,
3,620✔
661
            input_utxos: salted_input_utxos,
3,620✔
662
            transaction_kernel,
3,620✔
663
        }
3,620✔
664
    }
3,620✔
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

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 |(
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
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())
124✔
744
                        {
745
                            if !release_date.is_zero() {
124✔
746
                                let time_lock_coin = TimeLock::until(*release_date);
91✔
747
                                let mut coins = utxo.coins().to_vec();
91✔
748
                                coins.push(time_lock_coin);
91✔
749
                                *utxo = (utxo.lock_script_hash(), coins).into()
91✔
750
                            }
33✔
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
                        );
760

761
                        // generate output UTXOs
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
                            );
768

769
                        // generate primitive transaction witness and time lock witness from there
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
                        )
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

783
                            transaction_primitive_witness.kernel = modified_kernel;
61✔
784
                            TimeLockWitness::from(transaction_primitive_witness)
61✔
785
                        })
61✔
786
                        .boxed()
61✔
787
                    },
61✔
788
                )
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
        )
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
            )
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
        )
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
            )
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 |(
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✔
UNCOV
883
                        maybe_coinbase_dist
×
884
                    } else {
885
                        None
212✔
886
                    };
887

888
                    // distribute total amount across inputs (+ coinbase)
889
                    let mut input_denominator = input_dist.iter().map(|u| *u as f64).sum::<f64>();
427✔
890
                    if let Some(d) = maybe_coinbase_dist {
212✔
UNCOV
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)
427✔
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)
427✔
900
                        .map(|f| NativeCurrencyAmount::try_from(f).unwrap())
427✔
901
                        .collect_vec();
212✔
902
                    let maybe_coinbase = if maybe_coinbase_dist.is_some()
212✔
903
                        || input_amounts.is_empty()
212✔
904
                    {
UNCOV
905
                        Some(
×
UNCOV
906
                            total_amount
×
UNCOV
907
                                .checked_sub(
×
UNCOV
908
                                    &input_amounts.iter().copied().sum::<NativeCurrencyAmount>(),
×
UNCOV
909
                                )
×
UNCOV
910
                                .unwrap(),
×
UNCOV
911
                        )
×
912
                    } else {
913
                        let sum_of_all_but_last = input_amounts
212✔
914
                            .iter()
212✔
915
                            .rev()
212✔
916
                            .skip(1)
212✔
917
                            .copied()
212✔
918
                            .sum::<NativeCurrencyAmount>();
212✔
919
                        *input_amounts.last_mut().unwrap() =
212✔
920
                            total_amount.checked_sub(&sum_of_all_but_last).unwrap();
212✔
921
                        None
212✔
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);
403✔
927
                    let output_weights = output_dist
212✔
928
                        .into_iter()
212✔
929
                        .map(|u| (u as f64) / output_denominator)
403✔
930
                        .collect_vec();
212✔
931
                    let output_amounts = output_weights
212✔
932
                        .into_iter()
212✔
933
                        .map(|w| total_amount.to_nau_f64() * w)
403✔
934
                        .map(|f| NativeCurrencyAmount::try_from(f).unwrap())
403✔
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

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

947
                    assert_eq!(
212✔
948
                        total_inputs + maybe_coinbase.unwrap_or(NativeCurrencyAmount::coins(0)),
212✔
949
                        total_outputs + fee
212✔
950
                    );
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
                        );
957
                    let mut counter = 0usize;
212✔
958
                    for utxo in &mut input_utxos {
639✔
959
                        let release_date = release_dates[counter];
427✔
960
                        let time_lock = TimeLock::until(release_date);
427✔
961
                        let mut coins = utxo.coins().to_vec();
427✔
962
                        coins.push(time_lock);
427✔
963
                        *utxo = Utxo::from((utxo.lock_script_hash(), coins));
427✔
964
                        counter += 1;
427✔
965
                    }
427✔
966
                    for utxo in &mut output_utxos {
615✔
967
                        let mut coins = utxo.coins().to_vec();
403✔
968
                        coins.push(TimeLock::until(release_dates[counter]));
403✔
969
                        *utxo = Utxo::from((utxo.lock_script_hash(), coins));
403✔
970
                        counter += 1;
403✔
971
                    }
403✔
972
                    let release_dates = release_dates.clone();
212✔
973

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
                    )
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

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

1000
#[cfg(test)]
1001
#[cfg_attr(coverage_nightly, coverage(off))]
1002
#[allow(clippy::explicit_deref_methods)] // suppress clippy's bad autosuggestion
1003
mod tests {
1004
    use proptest::collection::vec;
1005
    use proptest::prelude::Arbitrary;
1006
    use proptest::prelude::Strategy;
1007
    use proptest::prop_assert;
1008
    use proptest::prop_assert_eq;
1009
    use proptest::strategy::Just;
1010
    use proptest::test_runner::TestRunner;
1011
    use proptest_arbitrary_interop::arb;
1012
    use tasm_lib::twenty_first::math::tip5::Tip5;
1013
    use test_strategy::proptest;
1014

1015
    use super::neptune_arbitrary::arbitrary_primitive_witness_with_active_timelocks;
1016
    use super::neptune_arbitrary::arbitrary_primitive_witness_with_expired_timelocks;
1017
    use super::*;
1018
    use crate::models::proof_abstractions::tasm::builtins as tasm;
1019
    use crate::models::proof_abstractions::tasm::program::tests::test_program_snapshot;
1020
    use crate::models::proof_abstractions::tasm::program::tests::ConsensusProgramSpecification;
1021

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

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

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

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

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

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

1058
            // iterate over inputs
1059
            let input_utxos = input_salted_utxos.utxos;
1060
            let mut i = 0;
1061
            while i < input_utxos.len() {
1062
                // get coins
1063
                let coins = input_utxos[i].coins();
1064

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

1073
                        // assert format
1074
                        assert!(state.len() == 1);
1075

1076
                        // extract timestamp
1077
                        let release_date: BFieldElement = state[0];
1078

1079
                        // test time lock
1080
                        assert!(release_date.value() < timestamp.value());
1081
                    }
1082
                    j += 1;
1083
                }
1084
                i += 1;
1085
            }
1086

1087
            return;
1088
        }
1089
    }
1090

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

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

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

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

1253
    #[proptest(cases = 5)]
1254
    fn primitive_witness_with_active_timelocks_is_invalid(
1255
        #[strategy(arb::<Timestamp>())] _now: Timestamp,
1256
        #[strategy(arbitrary_primitive_witness_with_active_timelocks(2, 2, 2, #_now))]
1257
        primitive_witness: PrimitiveWitness,
1258
    ) {
1259
        // Negative test: Primitive witness spending inputs that are timelocked
1260
        // must fail to validate.
1261
        let rt = crate::tests::tokio_runtime();
1262
        prop_assert!(rt.block_on(primitive_witness.validate()).is_err());
1263
    }
1264

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

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

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

1301
        let rust_result = TimeLock.run_rust(
1302
            &time_lock_witness.standard_input(),
1303
            time_lock_witness.nondeterminism(),
1304
        );
1305
        prop_assert!(
1306
            rust_result.is_ok(),
1307
            "time lock program did not halt gracefully"
1308
        );
1309
        let tasm_result = TimeLock.run_tasm(
1310
            &time_lock_witness.standard_input(),
1311
            time_lock_witness.nondeterminism(),
1312
        );
1313
        prop_assert!(
1314
            tasm_result.is_ok(),
1315
            "time lock program did not halt gracefully"
1316
        );
1317
        prop_assert_eq!(tasm_result.unwrap(), rust_result.unwrap());
1318
    }
1319

1320
    test_program_snapshot!(
1321
        TimeLock,
1322
        // snapshot taken from master on 2025-04-11 e2a712efc34f78c6a28801544418e7051127d284
1323
        "4b4d251947a07f9f2c016c1c271c04ce41013ff50031bd42854919be6e0e4849ebf931e856b542ad"
1324
    );
1325
}
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