Skip to content

Commit d4626fb

Browse files
authored
Merge branch 'main' into cleanup
2 parents 26cfb85 + dfed827 commit d4626fb

File tree

5 files changed

+136
-0
lines changed

5 files changed

+136
-0
lines changed

crates/move-stackless-bytecode/src/pure_callee_detection.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ impl FunctionTargetProcessor for PureCalleeDetectionProcessor {
4141
if fun_env.is_native() || fun_env.is_intrinsic() {
4242
continue;
4343
}
44+
// Skip functions with loop invariants: MoveLoopInvariantsProcessor
45+
// injects `ensures` calls into their bytecode, which is incompatible
46+
// with pure callee validation. TODO: fix MoveLoopInvariantsProcessor
47+
// to handle pure callee targets without injecting `ensures`.
48+
if targets.get_loop_invariants(&callee).is_some() {
49+
continue;
50+
}
4451
targets.add_pure_callee(callee);
4552
queue.push_back(callee);
4653
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#[allow(unused_field)]
2+
module 0x42::map_ref_struct;
3+
4+
use prover::prover::{ensures, forall};
5+
use prover::vector_iter::map;
6+
7+
public struct Entry has copy, drop, store {
8+
id: u64,
9+
amount: u64,
10+
}
11+
12+
public struct MySet has drop {
13+
validators: vector<Entry>,
14+
}
15+
16+
#[ext(pure)]
17+
fun get_id(e: &Entry): u64 {
18+
e.id
19+
}
20+
21+
/// The function under test: map_ref over struct field
22+
public fun active_ids(set: &MySet): vector<u64> {
23+
let mut r = vector[];
24+
let mut i = 0;
25+
let len = set.validators.length();
26+
while (i < len) {
27+
r.push_back(set.validators[i].id);
28+
i = i + 1;
29+
};
30+
r
31+
}
32+
33+
#[spec_only(loop_inv(target = active_ids)), ext(pure)]
34+
fun active_ids_invariant(i: u64, len: u64, set: &MySet, r: &vector<u64>): bool {
35+
i <= len
36+
&& len == set.validators.length()
37+
&& r.length() == i
38+
&& forall!(|j| mapped_to_i(*j, i, &set.validators, r))
39+
}
40+
41+
#[ext(pure)]
42+
fun mapped_to_i(j: u64, i: u64, v: &vector<Entry>, r: &vector<u64>): bool {
43+
r.length() >= i &&
44+
v.length() >= i &&
45+
(j >= i || r[j] == v[j].id)
46+
}
47+
48+
#[spec(prove)]
49+
fun active_ids_spec(set: &MySet): vector<u64> {
50+
let r = active_ids(set);
51+
ensures(r == map!(&set.validators, |e| get_id(e)));
52+
r
53+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#[allow(unused_field, unused_function)]
2+
module 0x42::pure_callee_with_loop_inv;
3+
4+
use prover::prover::{ensures, forall};
5+
use prover::vector_iter::map;
6+
7+
public struct Entry has copy, drop, store {
8+
id: u64,
9+
amount: u64,
10+
}
11+
12+
public struct MySet has drop {
13+
validators: vector<Entry>,
14+
}
15+
16+
#[ext(pure)]
17+
fun get_id(e: &Entry): u64 {
18+
e.id
19+
}
20+
21+
/// Function with a loop invariant attached
22+
public fun active_ids(set: &MySet): vector<u64> {
23+
let mut r = vector[];
24+
let mut i = 0;
25+
let len = set.validators.length();
26+
while (i < len) {
27+
r.push_back(set.validators[i].id);
28+
i = i + 1;
29+
};
30+
r
31+
}
32+
33+
#[spec_only(loop_inv(target = active_ids)), ext(no_abort)]
34+
fun active_ids_invariant(i: u64, len: u64, set: &MySet, r: &vector<u64>): bool {
35+
i <= len
36+
&& len == set.validators.length()
37+
&& r.length() == i
38+
&& forall!(|j| mapped_to_i(*j, i, &set.validators, r))
39+
}
40+
41+
#[ext(pure)]
42+
fun mapped_to_i(j: u64, i: u64, v: &vector<Entry>, r: &vector<u64>): bool {
43+
r.length() >= i &&
44+
v.length() >= i &&
45+
(j >= i || r[j] == v[j].id)
46+
}
47+
48+
/// A pure function that calls active_ids — this should fail because
49+
/// active_ids has loop invariants which inject `ensures` into its bytecode,
50+
/// making it incompatible with pure callee requirements.
51+
#[ext(pure)]
52+
fun uses_active_ids(set: &MySet): bool {
53+
active_ids(set).length() > 0
54+
}
55+
56+
#[spec(prove)]
57+
fun active_ids_spec(set: &MySet): vector<u64> {
58+
let r = active_ids(set);
59+
ensures(r == map!(&set.validators, |e| get_id(e)));
60+
r
61+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: crates/sui-prover/tests/integration.rs
3+
expression: output
4+
---
5+
Verification successful
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
source: crates/sui-prover/tests/integration.rs
3+
expression: output
4+
---
5+
exiting with bytecode transformation errors
6+
error: Function 'pure_callee_with_loop_inv::active_ids' can't be used in pure functions. Try marking it with #[ext(pure)] attribute.
7+
┌─ tests/inputs/loop_invariant/pure_callee_with_loop_inv.fail.move:53:5
8+
9+
53active_ids(set).length() > 0
10+
^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)