Skip to content

Commit e2d3ea4

Browse files
feat(apollo_starknet_os_program): copy OS cairo code
1 parent 5ec4121 commit e2d3ea4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+10973
-0
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// An helper function to extract selected_ptrs from all_ptrs according to the builtin encodings
2+
// that appear in the selected_encodings list.
3+
// The caller needs to pass n_selected_builtins as a hint.
4+
// Returns a pointer to the next memory slot after the selected_encodings list, see "Assumptions".
5+
//
6+
// For example, given the following setup:
7+
// - all_encodings points to ["output", "pedersen", "range-check"].
8+
// - selected_encodings points to ["output", "range-check"]
9+
// - all_ptrs points to [output_ptr, pedersen_ptr, range_check_ptr]
10+
// - The caller asserts that the return value is selected_encodings + n_selected_builtins(2).
11+
// The function will check that selected_encodings points to [output_ptr, range_check_ptr].
12+
//
13+
// n_builtins is the length of the list of *all pointers*.
14+
// Assumptions:
15+
// * The caller has to check that n_selected_builtins = selected_encodings_end - selected_encodings.
16+
// * All lists are sorted according to the order of builtins input in Cairo programs.
17+
// * len(selected_encodings) <= len(all_encodings) == len(all_ptrs).
18+
func inner_select_builtins(
19+
all_encodings: felt*,
20+
all_ptrs: felt*,
21+
selected_encodings: felt*,
22+
selected_ptrs: felt*,
23+
n_builtins,
24+
) -> (selected_encodings_end: felt*) {
25+
// Number of memory cells used when n_builtins = 0.
26+
const FUNC_MEMORY_NO_BUILTINS = 1;
27+
// Number of memory cells used *in a single iteration* when n_builtins > 0.
28+
const FUNC_MEMORY_WITH_BUILTINS = 10;
29+
30+
if (n_builtins == 0) {
31+
// Return a pointer to the end of the selected_encodings list.
32+
return (selected_encodings_end=selected_encodings);
33+
}
34+
35+
alloc_locals;
36+
// select_builtin equals 1 if the first builtin should be selected and 0 otherwise.
37+
local select_builtin;
38+
%{
39+
# A builtin should be selected iff its encoding appears in the selected encodings list
40+
# and the list wasn't exhausted.
41+
# Note that testing inclusion by a single comparison is possible since the lists are sorted.
42+
ids.select_builtin = int(
43+
n_selected_builtins > 0 and memory[ids.selected_encodings] == memory[ids.all_encodings])
44+
if ids.select_builtin:
45+
n_selected_builtins = n_selected_builtins - 1
46+
%}
47+
// Verify that select_builtin is a bit.
48+
select_builtin = select_builtin * select_builtin;
49+
50+
local curr_builtin_encoding = [all_encodings];
51+
local curr_builtin_ptr = [all_ptrs];
52+
53+
if (select_builtin != 0) {
54+
// Verify that the current builtin is indeed selected, by asserting that its encoding
55+
// appears in the selected encodings list.
56+
curr_builtin_encoding = [selected_encodings];
57+
// Copy the current builtin pointer between selected_ptrs and all_ptrs.
58+
curr_builtin_ptr = [selected_ptrs];
59+
}
60+
61+
// Advance all list pointers accordingly and continue selection by calling inner_select_builtins
62+
// recursively.
63+
// Lists of selected builtins/encodings should advance only if the current builtin was selected.
64+
return inner_select_builtins(
65+
all_encodings=all_encodings + 1,
66+
all_ptrs=all_ptrs + 1,
67+
selected_encodings=selected_encodings + select_builtin,
68+
selected_ptrs=selected_ptrs + select_builtin,
69+
n_builtins=n_builtins - 1,
70+
);
71+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from starkware.cairo.builtin_selection.inner_select_builtins import inner_select_builtins
2+
3+
// A wrapper for 'inner_select_builtins' function (see its documentation).
4+
func select_builtins(
5+
n_builtins,
6+
all_encodings: felt*,
7+
all_ptrs: felt*,
8+
n_selected_builtins,
9+
selected_encodings: felt*,
10+
selected_ptrs: felt*,
11+
) {
12+
%{ vm_enter_scope({'n_selected_builtins': ids.n_selected_builtins}) %}
13+
let (selected_encodings_end) = inner_select_builtins(
14+
all_encodings=all_encodings,
15+
all_ptrs=all_ptrs,
16+
selected_encodings=selected_encodings,
17+
selected_ptrs=selected_ptrs,
18+
n_builtins=n_builtins,
19+
);
20+
%{ vm_exit_scope() %}
21+
// Assert that the correct number of builtins was selected.
22+
assert n_selected_builtins = selected_encodings_end - selected_encodings;
23+
24+
return ();
25+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from starkware.cairo.builtin_selection.inner_select_builtins import inner_select_builtins
2+
from starkware.cairo.common.registers import get_fp_and_pc
3+
4+
// A wrapper for 'inner_select_builtins' function (see its documentation).
5+
// Returns the selected builtin pointers (e.g., if n_selected_builtins=2, returns two values).
6+
func select_input_builtins(
7+
all_encodings: felt*,
8+
all_ptrs: felt*,
9+
n_all_builtins: felt,
10+
selected_encodings: felt*,
11+
n_selected_builtins,
12+
) {
13+
// Number of memory cells used, without taking the inner function memory into account.
14+
const FUNC_MEMORY_WITHOUT_INNER_FUNC = 11;
15+
const INNER_FUNC_MEMORY_PER_ITERATION = inner_select_builtins.FUNC_MEMORY_WITH_BUILTINS;
16+
const INNER_FUNC_MEMORY_FINAL_ITERATION = inner_select_builtins.FUNC_MEMORY_NO_BUILTINS;
17+
// 'inner_select_builtins' has n_all_builtins iterations, until the final halting one, when
18+
// called with n_builtins = n_all_builtins.
19+
let inner_func_memory = n_all_builtins * INNER_FUNC_MEMORY_PER_ITERATION +
20+
INNER_FUNC_MEMORY_FINAL_ITERATION;
21+
let total_func_memory = inner_func_memory + FUNC_MEMORY_WITHOUT_INNER_FUNC;
22+
23+
let frame = call get_fp_and_pc;
24+
// The selected builtin pointers are the return values at the end of the function memory.
25+
let selected_ptrs = frame.fp_val + total_func_memory;
26+
%{ vm_enter_scope({'n_selected_builtins': ids.n_selected_builtins}) %}
27+
let inner_ret = inner_select_builtins(
28+
all_encodings=all_encodings,
29+
all_ptrs=all_ptrs,
30+
selected_encodings=selected_encodings,
31+
selected_ptrs=selected_ptrs,
32+
n_builtins=n_all_builtins,
33+
);
34+
%{ vm_exit_scope() %}
35+
// Assert that the correct number of builtins was selected.
36+
n_selected_builtins = inner_ret.selected_encodings_end - selected_encodings;
37+
38+
ap += n_selected_builtins;
39+
ret;
40+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Validates that the builtin pointer of a single builtin was advanced correctly.
2+
// The inputs are:
3+
// The previous builtin pointer.
4+
// The new builtin pointer.
5+
// The size of the builtin instances.
6+
// The function validates that the difference between the new builtin pointer and the old builtin
7+
// pointer is a positive integer divisible by the given builtin instance size.
8+
//
9+
// The function consumes 1 range check instance starting at range_check_ptr and returns the
10+
// updated range check pointer.
11+
func validate_builtin{range_check_ptr}(
12+
prev_builtin_ptr: felt*, new_builtin_ptr: felt*, builtin_instance_size: felt
13+
) {
14+
// Check that the difference is positive and divisible by builtin_instance_size by checking that
15+
// 0 <= div_res < RANGE_CHECK_BOUND and diff = div_res * builtin_instance_size.
16+
tempvar diff = new_builtin_ptr - prev_builtin_ptr;
17+
tempvar div_res = diff / builtin_instance_size;
18+
div_res = [range_check_ptr];
19+
let range_check_ptr = range_check_ptr + 1;
20+
return ();
21+
}
22+
23+
// Validates that the builtin pointers were advanced correctly.
24+
//
25+
// The inputs are:
26+
// The previous list of builtin pointers.
27+
// The new list of builtin pointers.
28+
// The sizes of the builtin instances.
29+
// The number of builtins.
30+
//
31+
// For each builtin the function validates that the difference between the new builtin pointer and
32+
// the old builtin pointer is a nonnegative integer divisible by the corresponding builtin
33+
// instance size.
34+
//
35+
// The function consumes n_builtins range check instances starting at range_check_ptr and returns
36+
// the updated range check pointer.
37+
func validate_builtins{range_check_ptr}(
38+
prev_builtin_ptrs: felt*, new_builtin_ptrs: felt*, builtin_instance_sizes: felt*, n_builtins
39+
) {
40+
if (n_builtins == 0) {
41+
return ();
42+
}
43+
44+
validate_builtin(
45+
prev_builtin_ptr=cast([prev_builtin_ptrs], felt*),
46+
new_builtin_ptr=cast([new_builtin_ptrs], felt*),
47+
builtin_instance_size=[builtin_instance_sizes],
48+
);
49+
50+
return validate_builtins(
51+
prev_builtin_ptrs=prev_builtin_ptrs + 1,
52+
new_builtin_ptrs=new_builtin_ptrs + 1,
53+
builtin_instance_sizes=builtin_instance_sizes + 1,
54+
n_builtins=n_builtins - 1,
55+
);
56+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
from starkware.cairo.common.alloc import alloc
2+
3+
// The segment arena builtin allows Sierra libfuncs to allocate memory segments and only track their
4+
// ends (rather than both the start and the end). When the segment is finalized, the arena can
5+
// provide the start pointer that corresponds to the segment (given its end).
6+
//
7+
// The builtin should be used as follows:
8+
// * Every segment must be allocated and finalized exactly once. This can be achieved by using
9+
// linear types (the object holding the segment must not be duplicatable nor droppable).
10+
// * Segment allocation:
11+
// * Allocates a new segment and updates `SegmentInfo::start` with the pointer.
12+
// * The segment should be temporary if `n_segments > 0`.
13+
// * Increases `n_segments` by 1.
14+
// * Segment finalization:
15+
// * Guesses the index of the segment, in the range [0, n_segments).
16+
// * Writes the end of the segment in `SegmentInfo::end`.
17+
// * Copies the current value of `n_finalized` to `SegmentInfo::finalization_index`.
18+
// * Increases `n_finalized` by 1.
19+
// * Checks that the segment size is nonnegative (range-check on `end - start`).
20+
21+
// Represents the information about a single segment allocated by the arena.
22+
struct SegmentInfo {
23+
// A pointer to the first element of this segment.
24+
start: felt*,
25+
// A pointer to the end of this segment (the first unused element).
26+
end: felt*,
27+
// A sequential id, assigned to the segment when it is finalized.
28+
// This value is used to guarantee that 'end' is not assigned twice.
29+
finalization_index: felt,
30+
}
31+
32+
// Represents the status of the segment arena.
33+
struct SegmentArenaBuiltin {
34+
// A pointer to a list of SegmentInfo. infos[i] contains information about the i-th segment
35+
// (ordered by construction).
36+
// The value is fixed during the execution of an entry point.
37+
infos: SegmentInfo*,
38+
// The number of segments that were created so far.
39+
n_segments: felt,
40+
// The number of segments that were finalized so far.
41+
n_finalized: felt,
42+
}
43+
44+
// Constructs a new segment for the segment arena builtin and initializes it with an empty instance
45+
// of `SegmentArenaBuiltin`.
46+
func new_arena() -> SegmentArenaBuiltin* {
47+
let (segment_arena: SegmentArenaBuiltin*) = alloc();
48+
assert segment_arena[0] = SegmentArenaBuiltin(
49+
infos=cast(nondet %{ segments.add() %}, SegmentInfo*), n_segments=0, n_finalized=0
50+
);
51+
return &segment_arena[1];
52+
}
53+
54+
// Validates the segment arena builtin.
55+
//
56+
// In particular, relocates the temporary segments such that the start of segment i is strictly
57+
// larger than the end of segment i+1.
58+
func validate_segment_arena(segment_arena: SegmentArenaBuiltin*) {
59+
tempvar n_segments = segment_arena.n_segments;
60+
tempvar n_finalized = segment_arena.n_finalized;
61+
// The following line should follow from the fact that every allocated segment
62+
// must be finalized exactly once.
63+
// We keep it both as a sanity check and since Sierra compilation is not proven yet.
64+
assert n_segments = n_finalized;
65+
66+
if (n_segments == 0) {
67+
return ();
68+
}
69+
70+
// The following call also implies that n_segments > 0.
71+
_verify_continuity(infos=segment_arena.infos, n_segments_minus_one=n_segments - 1);
72+
return ();
73+
}
74+
75+
// Helper function for validate_segment_arena.
76+
func _verify_continuity(infos: SegmentInfo*, n_segments_minus_one: felt) {
77+
if (n_segments_minus_one == 0) {
78+
// If there is only one segment left, there is no need to check anything.
79+
return ();
80+
}
81+
82+
// Enforce an empty cell between two consecutive segments so that the start of a segment
83+
// is strictly bigger than the end of the previous segment.
84+
// This is required for proving the soundness of this construction, in the case where a segment
85+
// has length zero.
86+
87+
// Note: the following code was copied from relocate_segment() for efficiency reasons.
88+
let src_ptr = infos[1].start;
89+
let dest_ptr = infos[0].end + 1;
90+
%{ memory.add_relocation_rule(src_ptr=ids.src_ptr, dest_ptr=ids.dest_ptr) %}
91+
assert src_ptr = dest_ptr;
92+
93+
return _verify_continuity(infos=&infos[1], n_segments_minus_one=n_segments_minus_one - 1);
94+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// The dummy caller address of an externally originated transaction.
2+
const ORIGIN_ADDRESS = 0;
3+
4+
// Transaction hash prefixes.
5+
const DECLARE_HASH_PREFIX = 'declare';
6+
const DEPLOY_HASH_PREFIX = 'deploy';
7+
const DEPLOY_ACCOUNT_HASH_PREFIX = 'deploy_account';
8+
const INVOKE_HASH_PREFIX = 'invoke';
9+
const L1_HANDLER_HASH_PREFIX = 'l1_handler';
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from starkware.cairo.common.math import assert_lt_felt, assert_not_zero
2+
3+
const ETH_ADDRESS_BOUND = 2 ** 160;
4+
5+
func assert_eth_address_range{range_check_ptr}(address: felt) {
6+
with_attr error_message("Invalid Ethereum address - value is more than 160 bits") {
7+
assert_lt_felt(address, ETH_ADDRESS_BOUND);
8+
}
9+
10+
with_attr error_message("Invalid Ethereum address - value is zero") {
11+
assert_not_zero(address);
12+
}
13+
return ();
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from starkware.starknet.common.syscalls import SEND_MESSAGE_TO_L1_SELECTOR, SendMessageToL1SysCall
2+
3+
// Sends a message to an L1 contract at 'l1_address' with given payload.
4+
func send_message_to_l1{syscall_ptr: felt*}(to_address: felt, payload_size: felt, payload: felt*) {
5+
assert [cast(syscall_ptr, SendMessageToL1SysCall*)] = SendMessageToL1SysCall(
6+
selector=SEND_MESSAGE_TO_L1_SELECTOR,
7+
to_address=to_address,
8+
payload_size=payload_size,
9+
payload_ptr=payload,
10+
);
11+
%{ syscall_handler.send_message_to_l1(segments=segments, syscall_ptr=ids.syscall_ptr) %}
12+
let syscall_ptr = syscall_ptr + SendMessageToL1SysCall.SIZE;
13+
return ();
14+
}

0 commit comments

Comments
 (0)