Skip to content

Commit 1db9047

Browse files
committed
signoff!: u32::safe_sub
BREAKING CHANGE: Remove the implementation of `DeprecatedSnippet` for `u32::safe_sub::SafeSub`, implement `BasicSnippet` directly.
1 parent b69af01 commit 1db9047

File tree

2 files changed

+99
-148
lines changed

2 files changed

+99
-148
lines changed
Lines changed: 98 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1,189 +1,139 @@
1-
use num::Zero;
2-
use rand::prelude::*;
1+
use std::collections::HashMap;
2+
33
use triton_vm::prelude::*;
44

5-
use crate::empty_stack;
65
use crate::prelude::*;
7-
use crate::traits::deprecated_snippet::DeprecatedSnippet;
8-
use crate::InitVmState;
9-
10-
#[derive(Clone, Debug)]
6+
use crate::traits::basic_snippet::Reviewer;
7+
use crate::traits::basic_snippet::SignOffFingerprint;
8+
9+
/// Subtract two `u32`s and crash on overflow.
10+
///
11+
/// ### Behavior
12+
///
13+
/// ```text
14+
/// BEFORE: _ [right: 32] [left: u32]
15+
/// AFTER: _ [left - right: u32]
16+
/// ```
17+
///
18+
/// ### Preconditions
19+
///
20+
/// - all input arguments are properly [`BFieldCodec`] encoded
21+
/// - `left` is greater than or equal to `right`
22+
///
23+
/// ### Postconditions
24+
///
25+
/// - the output is properly [`BFieldCodec`] encoded
26+
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
1127
pub struct SafeSub;
1228

13-
impl DeprecatedSnippet for SafeSub {
14-
fn entrypoint_name(&self) -> String {
15-
"tasmlib_arithmetic_u32_safe_sub".to_string()
16-
}
17-
18-
fn input_field_names(&self) -> Vec<String> {
19-
vec!["rhs".to_string(), "lhs".to_string()]
20-
}
21-
22-
fn input_types(&self) -> Vec<DataType> {
23-
vec![DataType::U32, DataType::U32]
24-
}
25-
26-
fn output_field_names(&self) -> Vec<String> {
27-
vec!["lhs - rhs".to_string()]
28-
}
29+
impl SafeSub {
30+
pub const OVERFLOW_ERROR_ID: i128 = 470;
31+
}
2932

30-
fn output_types(&self) -> Vec<DataType> {
31-
vec![DataType::U32]
33+
impl BasicSnippet for SafeSub {
34+
fn inputs(&self) -> Vec<(DataType, String)> {
35+
["right", "left"]
36+
.map(|s| (DataType::U32, s.to_string()))
37+
.to_vec()
3238
}
3339

34-
fn stack_diff(&self) -> isize {
35-
-1
40+
fn outputs(&self) -> Vec<(DataType, String)> {
41+
vec![(DataType::U32, "left - right".to_string())]
3642
}
3743

38-
fn function_code(&self, _library: &mut crate::library::Library) -> String {
39-
let entrypoint = self.entrypoint_name();
40-
format!(
41-
"
42-
// BEFORE: _ rhs lhs
43-
// AFTER: _ (lhs - rhs)
44-
{entrypoint}:
45-
swap 1
46-
push -1
47-
mul
48-
add
49-
dup 0 // _ (lhs - rhs) (lhs - rhs)
50-
split // _ (lhs - rhs) hi lo
51-
pop 1 // _ (lhs - rhs) hi
52-
push 0 // _ (lhs - rhs) hi 0
53-
eq // _ (lhs - rhs) (hi == 0)
54-
assert // _ (lhs - rhs)
55-
return
56-
"
57-
)
58-
}
59-
60-
fn crash_conditions(&self) -> Vec<String> {
61-
vec!["u32 overflow".to_string()]
62-
}
63-
64-
fn gen_input_states(&self) -> Vec<InitVmState> {
65-
let mut ret: Vec<InitVmState> = vec![];
66-
for _ in 0..10 {
67-
let mut stack = empty_stack();
68-
let lhs = thread_rng().gen_range(0..u32::MAX / 2);
69-
let rhs = thread_rng().gen_range(0..=lhs);
70-
let lhs = BFieldElement::new(lhs as u64);
71-
let rhs = BFieldElement::new(rhs as u64);
72-
stack.push(rhs);
73-
stack.push(lhs);
74-
ret.push(InitVmState::with_stack(stack));
75-
}
76-
77-
ret
78-
}
79-
80-
fn common_case_input_state(&self) -> InitVmState {
81-
InitVmState::with_stack(
82-
[
83-
empty_stack(),
84-
vec![BFieldElement::new(1 << 15), BFieldElement::new(1 << 16)],
85-
]
86-
.concat(),
87-
)
44+
fn entrypoint(&self) -> String {
45+
"tasmlib_arithmetic_u32_safe_sub".to_string()
8846
}
8947

90-
fn worst_case_input_state(&self) -> InitVmState {
91-
InitVmState::with_stack(
92-
[
93-
empty_stack(),
94-
vec![BFieldElement::zero(), BFieldElement::new((1 << 32) - 1)],
95-
]
96-
.concat(),
48+
fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
49+
triton_asm!(
50+
// BEFORE: _ right left
51+
// AFTER: _ (left - right)
52+
{self.entrypoint()}:
53+
pick 1
54+
push -1
55+
mul
56+
add
57+
dup 0 // _ diff diff
58+
split // _ diff hi lo
59+
pop 1 // _ diff hi
60+
push 0 // _ diff hi 0
61+
eq // _ diff (hi == 0)
62+
assert error_id {Self::OVERFLOW_ERROR_ID}
63+
return
9764
)
9865
}
9966

100-
fn rust_shadowing(
101-
&self,
102-
stack: &mut Vec<BFieldElement>,
103-
_std_in: Vec<BFieldElement>,
104-
_secret_in: Vec<BFieldElement>,
105-
_memory: &mut std::collections::HashMap<BFieldElement, BFieldElement>,
106-
) {
107-
let lhs: u32 = stack.pop().unwrap().try_into().unwrap();
108-
let rhs: u32 = stack.pop().unwrap().try_into().unwrap();
109-
110-
let diff = lhs - rhs;
111-
stack.push(BFieldElement::new(diff as u64));
67+
fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
68+
let mut sign_offs = HashMap::new();
69+
sign_offs.insert(Reviewer("ferdinand"), 0x40fd42d47d3b4bf4.into());
70+
sign_offs
11271
}
11372
}
11473

11574
#[cfg(test)]
11675
mod tests {
117-
use std::collections::HashMap;
118-
11976
use super::*;
120-
use crate::test_helpers::test_rust_equivalence_given_input_values_deprecated;
121-
use crate::test_helpers::test_rust_equivalence_multiple_deprecated;
77+
use crate::test_prelude::*;
12278

123-
#[test]
124-
fn snippet_test() {
125-
test_rust_equivalence_multiple_deprecated(&SafeSub, true);
126-
}
79+
impl Closure for SafeSub {
80+
type Args = (u32, u32);
12781

128-
#[test]
129-
fn safe_sub_simple_test() {
130-
prop_safe_sub(1000, 1, Some(999));
131-
prop_safe_sub(10_000, 900, Some(9_100));
132-
prop_safe_sub(123, 123, Some(0));
133-
prop_safe_sub(1230, 230, Some(1000));
134-
prop_safe_sub(1 << 31, 1 << 30, Some(1 << 30));
135-
prop_safe_sub(u32::MAX, 0, Some(u32::MAX));
136-
prop_safe_sub(u32::MAX, u32::MAX, Some(0));
137-
}
82+
fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) {
83+
let (right, left) = pop_encodable::<Self::Args>(stack);
84+
let diff = left.checked_sub(right).unwrap();
85+
push_encodable(stack, &diff);
86+
}
13887

139-
#[should_panic]
140-
#[test]
141-
fn overflow_test() {
142-
prop_safe_sub(1 << 31, (1 << 31) + 1000, None);
143-
}
88+
fn pseudorandom_args(
89+
&self,
90+
seed: [u8; 32],
91+
bench_case: Option<BenchmarkCase>,
92+
) -> Self::Args {
93+
let Some(bench_case) = bench_case else {
94+
let mut rng = StdRng::from_seed(seed);
95+
let left = rng.gen();
96+
let right = rng.gen_range(0..=left);
97+
98+
return (right, left);
99+
};
100+
101+
match bench_case {
102+
BenchmarkCase::CommonCase => (1 << 15, 1 << 16),
103+
BenchmarkCase::WorstCase => (0, u32::MAX),
104+
}
105+
}
144106

145-
#[should_panic]
146-
#[test]
147-
fn overflow_test_2() {
148-
prop_safe_sub(0, 1, None);
107+
fn corner_case_args(&self) -> Vec<Self::Args> {
108+
vec![(0, 0), (0, u32::MAX), (u32::MAX, u32::MAX)]
109+
}
149110
}
150111

151-
#[should_panic]
152112
#[test]
153-
fn overflow_test_3() {
154-
prop_safe_sub(0, u32::MAX, None);
113+
fn rust_shadow() {
114+
ShadowedClosure::new(SafeSub).test();
155115
}
156116

157-
fn prop_safe_sub(lhs: u32, rhs: u32, _expected: Option<u32>) {
158-
let mut init_stack = empty_stack();
159-
init_stack.push(BFieldElement::new(rhs as u64));
160-
init_stack.push(BFieldElement::new(lhs as u64));
161-
let expected = lhs.checked_sub(rhs);
162-
let expected = [
163-
empty_stack(),
164-
vec![expected
165-
.map(|x| BFieldElement::new(x as u64))
166-
.unwrap_or_else(BFieldElement::zero)],
167-
]
168-
.concat();
169-
170-
test_rust_equivalence_given_input_values_deprecated(
171-
&SafeSub,
172-
&init_stack,
173-
&[],
174-
HashMap::default(),
175-
Some(&expected),
176-
);
117+
#[proptest]
118+
fn overflow_crashes_vm(
119+
#[filter(#left != u32::MAX)] left: u32,
120+
#[strategy(#left..)] right: u32,
121+
) {
122+
test_assertion_failure(
123+
&ShadowedClosure::new(SafeSub),
124+
InitVmState::with_stack(SafeSub.set_up_test_stack((right, left))),
125+
&[SafeSub::OVERFLOW_ERROR_ID],
126+
)
177127
}
178128
}
179129

180130
#[cfg(test)]
181131
mod benches {
182132
use super::*;
183-
use crate::snippet_bencher::bench_and_write;
133+
use crate::test_prelude::*;
184134

185135
#[test]
186136
fn safe_sub_benchmark() {
187-
bench_and_write(SafeSub);
137+
ShadowedClosure::new(SafeSub).bench();
188138
}
189139
}

tasm-lib/src/assertion_error_ids.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,4 @@ often.
5656
| 440..450 | [`u64::Incr`](arithmetic/u64/incr.rs) |
5757
| 450..460 | [`u32::SafeAdd`](arithmetic/u32/safe_add.rs) |
5858
| 460..470 | [`u32::SafeMul`](arithmetic/u32/safe_mul.rs) |
59+
| 470..480 | [`u32::SafeSub`](arithmetic/u32/safe_sub.rs) |

0 commit comments

Comments
 (0)