diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1b26a6d0d2..0974c30fbb 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -46,6 +46,7 @@ jobs: cairo_bench_programs, cairo_proof_programs, cairo_test_programs, + cairo_stwo_exclusive_programs, cairo_1_test_contracts, cairo_2_test_contracts, ] @@ -124,6 +125,12 @@ jobs: path: ${{ env.CAIRO_PROGRAMS_PATH }} key: cairo_test_programs-cache-${{ hashFiles('cairo_programs/**/*.cairo', 'examples/wasm-demo/src/array_sum.cairo') }} fail-on-cache-miss: true + - name: Fetch cairo stwo exclusive programs + uses: actions/cache/restore@v3 + with: + path: ${{ env.CAIRO_PROGRAMS_PATH }} + key: cairo_stwo_exclusive_programs-cache-${{ hashFiles('cairo_programs/**/*.cairo', 'examples/wasm-demo/src/array_sum.cairo') }} + fail-on-cache-miss: true - name: Fetch proof programs uses: actions/cache/restore@v3.4.0 with: @@ -486,6 +493,9 @@ jobs: - program-target: cairo_test_programs programs-dir: cairo_programs extra-args: '--cairo_pie_output {program}.rs.pie.zip' + - program-target: cairo_stwo_exclusive_programs + programs-dir: cairo_programs/stwo_exclusive_programs + extra-args: '--cairo_pie_output {program}.rs.pie.zip' name: Compute memory and execution traces with cairo-vm needs: [ build-programs, build-release ] runs-on: ubuntu-22.04 diff --git a/CHANGELOG.md b/CHANGELOG.md index be252bc857..f55eb9b542 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,22 @@ #### Upcoming Changes +* feat: implement `Blake2s` opcode in VM [#1927](https://github.com/lambdaclass/cairo-vm/pull/1927) + +* feat: remove `NonZeroReservedBits` from `VirtualMachineError` [#1948](https://github.com/lambdaclass/cairo-vm/pull/1948) + +* feat: set `encoded_instruction` to be u128 for opcode_extensions to come [#1940](https://github.com/lambdaclass/cairo-vm/pull/1940) + +* feat: add `get_u32_range` to `impl VirtualMachine` add `get_u32` and `get_u32_range` to `impl Memory` [#1936](https://github.com/lambdaclass/cairo-vm/pull/1936) + +* feat: add the field `opcode_extension` to the structure of `Instruction` [#1933](https://github.com/lambdaclass/cairo-vm/pull/1933) + * fix(BREAKING): Fix no trace padding flow in proof mode [#1909](https://github.com/lambdaclass/cairo-vm/pull/1909) * refactor: Limit ret opcode decodeing to Cairo0's standards. [#1925](https://github.com/lambdaclass/cairo-vm/pull/1925) +* feat: define HashMap of hint groups along with hint strings [#1943](https://github.com/lambdaclass/cairo-vm/pull/1943) + #### [2.0.0-rc4] - 2025-01-23 * feat: implement `kzg` data availability hints [#1887](https://github.com/lambdaclass/cairo-vm/pull/1887) diff --git a/CODEOWNERS b/CODEOWNERS index 4f9f94059e..fc21d87834 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @igaray @Oppen @fmoletta @juanbono @pefontana @gabrielbosio +* @igaray @Oppen @fmoletta @juanbono @pefontana @gabrielbosio @yuvalsw diff --git a/Cargo.lock b/Cargo.lock index 52fb70192f..822d666866 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -933,6 +933,7 @@ dependencies = [ "hashbrown 0.15.2", "hex", "iai-callgrind", + "indoc", "keccak", "lazy_static", "mimalloc", diff --git a/Cargo.toml b/Cargo.toml index f0ac0c0f67..35ee36aac0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ starknet-crypto = { version = "0.7.3", default-features = false, features = [ "alloc", ] } sha3 = { version = "0.10.8", default-features = false } +indoc = { version = "2.0.5", default-features = false } lazy_static = { version = "1.4.0", default-features = false, features = [ "spin_no_std", ] } diff --git a/Makefile b/Makefile index 9b60105ab3..5d772dbb40 100644 --- a/Makefile +++ b/Makefile @@ -82,6 +82,10 @@ CAIRO_RS_MEM:=$(patsubst $(TEST_DIR)/%.json, $(TEST_DIR)/%.rs.memory, $(COMPILED CAIRO_RS_TRACE:=$(patsubst $(TEST_DIR)/%.json, $(TEST_DIR)/%.rs.trace, $(COMPILED_TESTS)) CAIRO_RS_PIE:=$(patsubst $(TEST_DIR)/%.json, $(TEST_DIR)/%.rs.pie.zip, $(COMPILED_TESTS)) +STWO_EXCLUSIVE_DIR=cairo_programs/stwo_exclusive_programs +STWO_EXCLUSIVE_FILES:=$(wildcard $(STWO_EXCLUSIVE_DIR)/*.cairo) +COMPILED_STWO_EXCLUSIVE_TESTS:=$(patsubst $(STWO_EXCLUSIVE_DIR)/%.cairo, $(STWO_EXCLUSIVE_DIR)/%.json, $(STWO_EXCLUSIVE_FILES)) + BENCH_DIR=cairo_programs/benchmarks BENCH_FILES:=$(wildcard $(BENCH_DIR)/*.cairo) COMPILED_BENCHES:=$(patsubst $(BENCH_DIR)/%.cairo, $(BENCH_DIR)/%.json, $(BENCH_FILES)) @@ -249,6 +253,7 @@ check: cairo_test_programs: $(COMPILED_TESTS) $(COMPILED_BAD_TESTS) $(COMPILED_NORETROCOMPAT_TESTS) $(COMPILED_PRINT_TESTS) $(COMPILED_MOD_BUILTIN_TESTS) $(COMPILED_SECP_CAIRO0_HINTS) $(COMPILED_KZG_DA_CAIRO0_HINTS) cairo_proof_programs: $(COMPILED_PROOF_TESTS) $(COMPILED_MOD_BUILTIN_PROOF_TESTS) +cairo_stwo_exclusive_programs: $(COMPILED_STWO_EXCLUSIVE_TESTS) cairo_bench_programs: $(COMPILED_BENCHES) cairo_1_test_contracts: $(CAIRO_1_COMPILED_CASM_CONTRACTS) cairo_2_test_contracts: $(CAIRO_2_COMPILED_CASM_CONTRACTS) diff --git a/cairo-vm-tracer/src/tracer_data.rs b/cairo-vm-tracer/src/tracer_data.rs index e2b02cfb23..6b739c3ba3 100644 --- a/cairo-vm-tracer/src/tracer_data.rs +++ b/cairo-vm-tracer/src/tracer_data.rs @@ -143,7 +143,7 @@ impl TracerData { let (instruction_encoding, _) = get_instruction_encoding(entry.pc, &memory, program.prime())?; - let instruction_encoding = instruction_encoding.to_u64(); + let instruction_encoding = instruction_encoding.to_u128(); if instruction_encoding.is_none() { return Err(TraceDataError::FailedToConvertInstructionEncoding); } diff --git a/cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo b/cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo new file mode 100644 index 0000000000..28332ade33 --- /dev/null +++ b/cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.cairo @@ -0,0 +1,163 @@ +%builtins range_check bitwise + +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.cairo_blake2s.blake2s import STATE_SIZE_FELTS, INPUT_BLOCK_FELTS, _get_sigma +from starkware.cairo.common.cairo_blake2s.packed_blake2s import N_PACKED_INSTANCES, blake2s_compress +from starkware.cairo.common.cairo_builtins import BitwiseBuiltin + +const COUNTER = 64; +const U32_MASK = 0xffffffff; + +// Tests the Blake2s opcode runner using a preexisting implementation within the repo as reference. +// The initial state, a random message of 64 bytes and counter are used as input. +// Both the opcode and the reference implementation are run on the same inputs and then their outputs are compared. +// Before comparing the outputs, it is verified that the opcode runner has written the output to the correct location. +func main{range_check_ptr, bitwise_ptr: BitwiseBuiltin*}() { + alloc_locals; + + let (local random_message) = alloc(); + assert random_message[0] = 930933030; + assert random_message[1] = 1766240503; + assert random_message[2] = 3660871006; + assert random_message[3] = 388409270; + assert random_message[4] = 1948594622; + assert random_message[5] = 3119396969; + assert random_message[6] = 3924579183; + assert random_message[7] = 2089920034; + assert random_message[8] = 3857888532; + assert random_message[9] = 929304360; + assert random_message[10] = 1810891574; + assert random_message[11] = 860971754; + assert random_message[12] = 1822893775; + assert random_message[13] = 2008495810; + assert random_message[14] = 2958962335; + assert random_message[15] = 2340515744; + + let (local input_state) = alloc(); + // Set the initial state to IV (IV[0] is modified). + assert input_state[0] = 0x6B08E647; // IV[0] ^ 0x01010020 (config: no key, 32 bytes output). + assert input_state[1] = 0xBB67AE85; + assert input_state[2] = 0x3C6EF372; + assert input_state[3] = 0xA54FF53A; + assert input_state[4] = 0x510E527F; + assert input_state[5] = 0x9B05688C; + assert input_state[6] = 0x1F83D9AB; + assert input_state[7] = 0x5BE0CD19; + static_assert STATE_SIZE_FELTS == 8; + + // Use the packed blake2s_compress to compute the output of the first instance. + let (sigma) = _get_sigma(); + let (local cairo_output) = alloc(); + blake2s_compress( + h=input_state, + message=random_message, + t0=COUNTER, + f0=0, + sigma=sigma, + output=cairo_output, + ); + + // Unpack the first instance of the blake2s_compress output (extract the first 32 bits). + assert bitwise_ptr[0].x = cairo_output[0]; + assert bitwise_ptr[0].y = U32_MASK; + assert bitwise_ptr[1].x = cairo_output[1]; + assert bitwise_ptr[1].y = U32_MASK; + assert bitwise_ptr[2].x = cairo_output[2]; + assert bitwise_ptr[2].y = U32_MASK; + assert bitwise_ptr[3].x = cairo_output[3]; + assert bitwise_ptr[3].y = U32_MASK; + assert bitwise_ptr[4].x = cairo_output[4]; + assert bitwise_ptr[4].y = U32_MASK; + assert bitwise_ptr[5].x = cairo_output[5]; + assert bitwise_ptr[5].y = U32_MASK; + assert bitwise_ptr[6].x = cairo_output[6]; + assert bitwise_ptr[6].y = U32_MASK; + assert bitwise_ptr[7].x = cairo_output[7]; + assert bitwise_ptr[7].y = U32_MASK; + + // Run the blake2s opcode runner on the same inputs and store its output. + let vm_output = run_blake2s( + dst=COUNTER, + op0=input_state, + op1=random_message, + ); + + // Verify that the opcode runner has written the 8 felts to the correct location. + tempvar check_nonempty = vm_output[0]; + tempvar check_nonempty = vm_output[1]; + tempvar check_nonempty = vm_output[2]; + tempvar check_nonempty = vm_output[3]; + tempvar check_nonempty = vm_output[4]; + tempvar check_nonempty = vm_output[5]; + tempvar check_nonempty = vm_output[6]; + tempvar check_nonempty = vm_output[7]; + + // Compare the vm_output to the blake2s_compress first instance output. + assert vm_output[0] = bitwise_ptr[0].x_and_y; + assert vm_output[1] = bitwise_ptr[1].x_and_y; + assert vm_output[2] = bitwise_ptr[2].x_and_y; + assert vm_output[3] = bitwise_ptr[3].x_and_y; + assert vm_output[4] = bitwise_ptr[4].x_and_y; + assert vm_output[5] = bitwise_ptr[5].x_and_y; + assert vm_output[6] = bitwise_ptr[6].x_and_y; + assert vm_output[7] = bitwise_ptr[7].x_and_y; + + let bitwise_ptr = bitwise_ptr + BitwiseBuiltin.SIZE * STATE_SIZE_FELTS; + + return (); +} + +// Forces the runner to execute the Blake2s with the given operands. +// op0 is a pointer to an array of 8 felts as u32 integers of the state. +// op1 is a pointer to an array of 16 felts as u32 integers of the messsage. +// dst is a felt representing a u32 of the counter. +// ap contains a pointer to an array of 8 felts as u32 integers of the output state. +// Those values are stored within addresses fp-5, fp-4 and fp-3 respectively. +// An instruction encoding is built from offsets -5, -4, -3 and flags which are all 0 except for +// those denoting uses of fp as the base for operand addresses and flag_opcode_blake (16th flag). +// The instruction is then written to [pc] and the runner is forced to execute Blake2s. +func run_blake2s( + dst: felt, + op0: felt*, + op1: felt*, +) -> felt* { + alloc_locals; + + // Set the offsets for the operands. + let offset0 = (2**15)-5; + let offset1 = (2**15)-4; + let offset2 = (2**15)-3; + static_assert dst == [fp -5]; + static_assert op0 == [fp -4]; + static_assert op1 == [fp -3]; + + // Set the flags for the instruction. + let flag_dst_base_fp = 1; + let flag_op0_base_fp = 1; + let flag_op1_imm = 0; + let flag_op1_base_fp = 1; + let flag_op1_base_ap = 0; + let flag_res_add = 0; + let flag_res_mul = 0; + let flag_PC_update_jump = 0; + let flag_PC_update_jump_rel = 0; + let flag_PC_update_jnz = 0; + let flag_ap_update_add = 0; + let flag_ap_update_add_1 = 0; + let flag_opcode_call = 0; + let flag_opcode_ret = 0; + let flag_opcode_assert_eq = 0; + let flag_opcode_blake2s = 1; + + // Build the instruction encoding. + let flag_num = flag_dst_base_fp+flag_op0_base_fp*(2**1)+flag_op1_imm*(2**2)+flag_op1_base_fp*(2**3)+flag_opcode_blake2s*(2**15); + let instruction_num = offset0 + offset1*(2**16) + offset2*(2**32) + flag_num*(2**48); + static_assert instruction_num==9226608988349300731; + + // Write the instruction to [pc] and point [ap] to the designated output. + let (local vm_output) = alloc(); + assert [ap] = cast(vm_output, felt); + dw 9226608988349300731; + + return cast([ap], felt*); +} diff --git a/vm/Cargo.toml b/vm/Cargo.toml index a01a0ce2c0..0fc15c5f4b 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -50,6 +50,7 @@ hex = { workspace = true } bincode = { workspace = true } starknet-crypto = { workspace = true } sha3 = { workspace = true } +indoc = { workspace = true } lazy_static = { workspace = true } nom = { workspace = true } sha2 = { workspace = true } diff --git a/vm/src/hint_processor/builtin_hint_processor/hint_code.rs b/vm/src/hint_processor/builtin_hint_processor/hint_code.rs index 27f3a7bb31..c8c5aaebc2 100644 --- a/vm/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/vm/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -1,44 +1,41 @@ -pub const ADD_SEGMENT: &str = "memory[ap] = segments.add()"; - -pub const VM_ENTER_SCOPE: &str = "vm_enter_scope()"; -pub const VM_EXIT_SCOPE: &str = "vm_exit_scope()"; - -pub const MEMCPY_ENTER_SCOPE: &str = "vm_enter_scope({'n': ids.len})"; -pub const MEMCPY_CONTINUE_COPYING: &str = r#"n -= 1 -ids.continue_copying = 1 if n > 0 else 0"#; - -pub const MEMSET_ENTER_SCOPE: &str = "vm_enter_scope({'n': ids.n})"; -pub const MEMSET_CONTINUE_LOOP: &str = r#"n -= 1 -ids.continue_loop = 1 if n > 0 else 0"#; - -pub const POW: &str = "ids.locs.bit = (ids.prev_locs.exp % PRIME) & 1"; - -pub const IS_NN: &str = "memory[ap] = 0 if 0 <= (ids.a % PRIME) < range_check_builtin.bound else 1"; -pub const IS_NN_OUT_OF_RANGE: &str = - "memory[ap] = 0 if 0 <= ((-ids.a - 1) % PRIME) < range_check_builtin.bound else 1"; -pub const IS_LE_FELT: &str = "memory[ap] = 0 if (ids.a % PRIME) <= (ids.b % PRIME) else 1"; -pub const IS_POSITIVE: &str = r#"from starkware.cairo.common.math_utils import is_positive +use indoc::indoc; + +use crate::define_hint_string_map; +use crate::stdlib::collections::HashMap; + +define_hint_string_map! { + HINT_CODES, +(ADD_SEGMENT, indoc! {r#"memory[ap] = segments.add()"#}), +(VM_ENTER_SCOPE, indoc! {r#"vm_enter_scope()"#}), +(VM_EXIT_SCOPE, indoc! {r#"vm_exit_scope()"#}), +(MEMCPY_ENTER_SCOPE, indoc! {r#"vm_enter_scope({'n': ids.len})"#}), +(MEMCPY_CONTINUE_COPYING, indoc! {r#"n -= 1 +ids.continue_copying = 1 if n > 0 else 0"#}), +(MEMSET_ENTER_SCOPE, indoc! {r#"vm_enter_scope({'n': ids.n})"#}), +(MEMSET_CONTINUE_LOOP, indoc! {r#"n -= 1 +ids.continue_loop = 1 if n > 0 else 0"#}), +(POW, indoc! {r#"ids.locs.bit = (ids.prev_locs.exp % PRIME) & 1"#}), +(IS_NN, indoc! {r#"memory[ap] = 0 if 0 <= (ids.a % PRIME) < range_check_builtin.bound else 1"#}), +(IS_NN_OUT_OF_RANGE, indoc! {r#"memory[ap] = 0 if 0 <= ((-ids.a - 1) % PRIME) < range_check_builtin.bound else 1"#}), +(IS_LE_FELT, indoc! {r#"memory[ap] = 0 if (ids.a % PRIME) <= (ids.b % PRIME) else 1"#}), +(IS_POSITIVE, indoc! {r#"from starkware.cairo.common.math_utils import is_positive ids.is_positive = 1 if is_positive( - value=ids.value, prime=PRIME, rc_bound=range_check_builtin.bound) else 0"#; - -pub const ASSERT_NN: &str = r#"from starkware.cairo.common.math_utils import assert_integer + value=ids.value, prime=PRIME, rc_bound=range_check_builtin.bound) else 0"#}), +(ASSERT_NN, indoc! {r#"from starkware.cairo.common.math_utils import assert_integer assert_integer(ids.a) -assert 0 <= ids.a % PRIME < range_check_builtin.bound, f'a = {ids.a} is out of range.'"#; - -pub const ASSERT_NOT_ZERO: &str = r#"from starkware.cairo.common.math_utils import assert_integer +assert 0 <= ids.a % PRIME < range_check_builtin.bound, f'a = {ids.a} is out of range.'"#}), +(ASSERT_NOT_ZERO, indoc! {r#"from starkware.cairo.common.math_utils import assert_integer assert_integer(ids.value) -assert ids.value % PRIME != 0, f'assert_not_zero failed: {ids.value} = 0.'"#; - -pub const ASSERT_NOT_EQUAL: &str = r#"from starkware.cairo.lang.vm.relocatable import RelocatableValue +assert ids.value % PRIME != 0, f'assert_not_zero failed: {ids.value} = 0.'"#}), +(ASSERT_NOT_EQUAL, indoc! {r#"from starkware.cairo.lang.vm.relocatable import RelocatableValue both_ints = isinstance(ids.a, int) and isinstance(ids.b, int) both_relocatable = ( isinstance(ids.a, RelocatableValue) and isinstance(ids.b, RelocatableValue) and ids.a.segment_index == ids.b.segment_index) assert both_ints or both_relocatable, \ f'assert_not_equal failed: non-comparable values: {ids.a}, {ids.b}.' -assert (ids.a - ids.b) % PRIME != 0, f'assert_not_equal failed: {ids.a} = {ids.b}.'"#; - -pub const ASSERT_LE_FELT: &str = r#"import itertools +assert (ids.a - ids.b) % PRIME != 0, f'assert_not_equal failed: {ids.a} = {ids.b}.'"#}), +(ASSERT_LE_FELT, indoc! {r#"import itertools from starkware.cairo.common.math_utils import assert_integer assert_integer(ids.a) @@ -56,17 +53,13 @@ excluded = lengths_and_indices[2][1] memory[ids.range_check_ptr + 1], memory[ids.range_check_ptr + 0] = ( divmod(lengths_and_indices[0][0], ids.PRIME_OVER_3_HIGH)) memory[ids.range_check_ptr + 3], memory[ids.range_check_ptr + 2] = ( - divmod(lengths_and_indices[1][0], ids.PRIME_OVER_2_HIGH))"#; - -pub const ASSERT_LE_FELT_V_0_6: &str = - "from starkware.cairo.common.math_utils import assert_integer + divmod(lengths_and_indices[1][0], ids.PRIME_OVER_2_HIGH))"#}), +(ASSERT_LE_FELT_V_0_6, "from starkware.cairo.common.math_utils import assert_integer assert_integer(ids.a) assert_integer(ids.b) assert (ids.a % PRIME) <= (ids.b % PRIME), \\ - f'a = {ids.a % PRIME} is not less than or equal to b = {ids.b % PRIME}.'"; - -pub const ASSERT_LE_FELT_V_0_8: &str = - "from starkware.cairo.common.math_utils import assert_integer + f'a = {ids.a % PRIME} is not less than or equal to b = {ids.b % PRIME}.'"), +(ASSERT_LE_FELT_V_0_8, indoc! {r#"from starkware.cairo.common.math_utils import assert_integer assert_integer(ids.a) assert_integer(ids.b) a = ids.a % PRIME @@ -74,65 +67,52 @@ b = ids.b % PRIME assert a <= b, f'a = {a} is not less than or equal to b = {b}.' ids.small_inputs = int( - a < range_check_builtin.bound and (b - a) < range_check_builtin.bound)"; - -pub const ASSERT_LE_FELT_EXCLUDED_0: &str = "memory[ap] = 1 if excluded != 0 else 0"; -pub const ASSERT_LE_FELT_EXCLUDED_1: &str = "memory[ap] = 1 if excluded != 1 else 0"; -pub const ASSERT_LE_FELT_EXCLUDED_2: &str = "assert excluded == 2"; - -pub const ASSERT_LT_FELT: &str = r#"from starkware.cairo.common.math_utils import assert_integer + a < range_check_builtin.bound and (b - a) < range_check_builtin.bound)"#}), +(ASSERT_LE_FELT_EXCLUDED_0, indoc! {r#"memory[ap] = 1 if excluded != 0 else 0"#}), +(ASSERT_LE_FELT_EXCLUDED_1, indoc! {r#"memory[ap] = 1 if excluded != 1 else 0"#}), +(ASSERT_LE_FELT_EXCLUDED_2, indoc! {r#"assert excluded == 2"#}), +(ASSERT_LT_FELT, indoc! {r#"from starkware.cairo.common.math_utils import assert_integer assert_integer(ids.a) assert_integer(ids.b) assert (ids.a % PRIME) < (ids.b % PRIME), \ - f'a = {ids.a % PRIME} is not less than b = {ids.b % PRIME}.'"#; - -pub const SPLIT_INT_ASSERT_RANGE: &str = - "assert ids.value == 0, 'split_int(): value is out of range.'"; - -pub const ASSERT_250_BITS: &str = r#"from starkware.cairo.common.math_utils import as_int + f'a = {ids.a % PRIME} is not less than b = {ids.b % PRIME}.'"#}), +(SPLIT_INT_ASSERT_RANGE, indoc! {r#"assert ids.value == 0, 'split_int(): value is out of range.'"#}), +(ASSERT_250_BITS, indoc! {r#"from starkware.cairo.common.math_utils import as_int # Correctness check. value = as_int(ids.value, PRIME) % PRIME assert value < ids.UPPER_BOUND, f'{value} is outside of the range [0, 2**250).' # Calculation for the assertion. -ids.high, ids.low = divmod(ids.value, ids.SHIFT)"#; - -pub const IS_250_BITS: &str = r#"ids.is_250 = 1 if ids.addr < 2**250 else 0"#; - -pub const IS_ADDR_BOUNDED: &str = r#"# Verify the assumptions on the relationship between 2**250, ADDR_BOUND and PRIME. +ids.high, ids.low = divmod(ids.value, ids.SHIFT)"#}), +(IS_250_BITS, indoc! {r#"ids.is_250 = 1 if ids.addr < 2**250 else 0"#}), +(IS_ADDR_BOUNDED, indoc! {r#"# Verify the assumptions on the relationship between 2**250, ADDR_BOUND and PRIME. ADDR_BOUND = ids.ADDR_BOUND % PRIME assert (2**250 < ADDR_BOUND <= 2**251) and (2 * 2**250 < PRIME) and ( ADDR_BOUND * 2 > PRIME), \ 'normalize_address() cannot be used with the current constants.' -ids.is_small = 1 if ids.addr < ADDR_BOUND else 0"#; - -pub const SPLIT_INT: &str = r#"memory[ids.output] = res = (int(ids.value) % PRIME) % ids.base -assert res < ids.bound, f'split_int(): Limb {res} is out of range.'"#; - -pub const SPLIT_64: &str = r#"ids.low = ids.a & ((1<<64) - 1) -ids.high = ids.a >> 64"#; - -pub const SPLIT_FELT: &str = r#"from starkware.cairo.common.math_utils import assert_integer +ids.is_small = 1 if ids.addr < ADDR_BOUND else 0"#}), +(SPLIT_INT, indoc! {r#"memory[ids.output] = res = (int(ids.value) % PRIME) % ids.base +assert res < ids.bound, f'split_int(): Limb {res} is out of range.'"#}), +(SPLIT_64, indoc! {r#"ids.low = ids.a & ((1<<64) - 1) +ids.high = ids.a >> 64"#}), +(SPLIT_FELT, indoc! {r#"from starkware.cairo.common.math_utils import assert_integer assert ids.MAX_HIGH < 2**128 and ids.MAX_LOW < 2**128 assert PRIME - 1 == ids.MAX_HIGH * 2**128 + ids.MAX_LOW assert_integer(ids.value) ids.low = ids.value & ((1 << 128) - 1) -ids.high = ids.value >> 128"#; - -pub const SQRT: &str = r#"from starkware.python.math_utils import isqrt +ids.high = ids.value >> 128"#}), +(SQRT, indoc! {r#"from starkware.python.math_utils import isqrt value = ids.value % PRIME assert value < 2 ** 250, f"value={value} is outside of the range [0, 2**250)." assert 2 ** 250 < PRIME -ids.root = isqrt(value)"#; - -pub const UNSIGNED_DIV_REM: &str = r#"from starkware.cairo.common.math_utils import assert_integer +ids.root = isqrt(value)"#}), +(UNSIGNED_DIV_REM, indoc! {r#"from starkware.cairo.common.math_utils import assert_integer assert_integer(ids.div) assert 0 < ids.div <= PRIME // range_check_builtin.bound, \ f'div={hex(ids.div)} is out of the valid range.' -ids.q, ids.r = divmod(ids.value, ids.div)"#; - -pub const SIGNED_DIV_REM: &str = r#"from starkware.cairo.common.math_utils import as_int, assert_integer +ids.q, ids.r = divmod(ids.value, ids.div)"#}), +(SIGNED_DIV_REM, indoc! {r#"from starkware.cairo.common.math_utils import as_int, assert_integer assert_integer(ids.div) assert 0 < ids.div <= PRIME // range_check_builtin.bound, \ @@ -148,18 +128,16 @@ q, ids.r = divmod(int_value, ids.div) assert -ids.bound <= q < ids.bound, \ f'{int_value} / {ids.div} = {q} is out of the range [{-ids.bound}, {ids.bound}).' -ids.biased_q = q + ids.bound"#; - -pub const IS_QUAD_RESIDUE: &str = r#"from starkware.crypto.signature.signature import FIELD_PRIME +ids.biased_q = q + ids.bound"#}), +(IS_QUAD_RESIDUE, indoc! {r#"from starkware.crypto.signature.signature import FIELD_PRIME from starkware.python.math_utils import div_mod, is_quad_residue, sqrt x = ids.x if is_quad_residue(x, FIELD_PRIME): ids.y = sqrt(x, FIELD_PRIME) else: - ids.y = sqrt(div_mod(x, 3, FIELD_PRIME), FIELD_PRIME)"#; - -pub const FIND_ELEMENT: &str = r#"array_ptr = ids.array_ptr + ids.y = sqrt(div_mod(x, 3, FIELD_PRIME), FIELD_PRIME)"#}), +(FIND_ELEMENT, indoc! {r#"array_ptr = ids.array_ptr elm_size = ids.elm_size assert isinstance(elm_size, int) and elm_size > 0, \ f'Invalid value for elm_size. Got: {elm_size}.' @@ -187,9 +165,8 @@ else: ids.index = i break else: - raise ValueError(f'Key {key} was not found.')"#; - -pub const SEARCH_SORTED_LOWER: &str = r#"array_ptr = ids.array_ptr + raise ValueError(f'Key {key} was not found.')"#}), +(SEARCH_SORTED_LOWER, indoc! {r#"array_ptr = ids.array_ptr elm_size = ids.elm_size assert isinstance(elm_size, int) and elm_size > 0, \ f'Invalid value for elm_size. Got: {elm_size}.' @@ -207,9 +184,8 @@ for i in range(n_elms): ids.index = i break else: - ids.index = n_elms"#; - -pub const SET_ADD: &str = r#"assert ids.elm_size > 0 + ids.index = n_elms"#}), +(SET_ADD, indoc! {r#"assert ids.elm_size > 0 assert ids.set_ptr <= ids.set_end_ptr elm_list = memory.get_range(ids.elm_ptr, ids.elm_size) for i in range(0, ids.set_end_ptr - ids.set_ptr, ids.elm_size): @@ -218,31 +194,26 @@ for i in range(0, ids.set_end_ptr - ids.set_ptr, ids.elm_size): ids.is_elm_in_set = 1 break else: - ids.is_elm_in_set = 0"#; - -pub const DEFAULT_DICT_NEW: &str = r#"if '__dict_manager' not in globals(): + ids.is_elm_in_set = 0"#}), +(DEFAULT_DICT_NEW, indoc! {r#"if '__dict_manager' not in globals(): from starkware.cairo.common.dict import DictManager __dict_manager = DictManager() -memory[ap] = __dict_manager.new_default_dict(segments, ids.default_value)"#; - -pub const DICT_NEW: &str = r#"if '__dict_manager' not in globals(): +memory[ap] = __dict_manager.new_default_dict(segments, ids.default_value)"#}), +(DICT_NEW, indoc! {r#"if '__dict_manager' not in globals(): from starkware.cairo.common.dict import DictManager __dict_manager = DictManager() memory[ap] = __dict_manager.new_dict(segments, initial_dict) -del initial_dict"#; - -pub const DICT_READ: &str = r#"dict_tracker = __dict_manager.get_tracker(ids.dict_ptr) +del initial_dict"#}), +(DICT_READ, indoc! {r#"dict_tracker = __dict_manager.get_tracker(ids.dict_ptr) dict_tracker.current_ptr += ids.DictAccess.SIZE -ids.value = dict_tracker.data[ids.key]"#; - -pub const DICT_WRITE: &str = r#"dict_tracker = __dict_manager.get_tracker(ids.dict_ptr) +ids.value = dict_tracker.data[ids.key]"#}), +(DICT_WRITE, indoc! {r#"dict_tracker = __dict_manager.get_tracker(ids.dict_ptr) dict_tracker.current_ptr += ids.DictAccess.SIZE ids.dict_ptr.prev_value = dict_tracker.data[ids.key] -dict_tracker.data[ids.key] = ids.new_value"#; - -pub const DICT_UPDATE: &str = r#"# Verify dict pointer and prev value. +dict_tracker.data[ids.key] = ids.new_value"#}), +(DICT_UPDATE, indoc! {r#"# Verify dict pointer and prev value. dict_tracker = __dict_manager.get_tracker(ids.dict_ptr) current_value = dict_tracker.data[ids.key] assert current_value == ids.prev_value, \ @@ -250,9 +221,8 @@ assert current_value == ids.prev_value, \ # Update value. dict_tracker.data[ids.key] = ids.new_value -dict_tracker.current_ptr += ids.DictAccess.SIZE"#; - -pub const SQUASH_DICT: &str = r#"dict_access_size = ids.DictAccess.SIZE +dict_tracker.current_ptr += ids.DictAccess.SIZE"#}), +(SQUASH_DICT, indoc! {r#"dict_access_size = ids.DictAccess.SIZE address = ids.dict_accesses.address_ assert ids.ptr_diff % dict_access_size == 0, \ 'Accesses array size must be divisible by DictAccess.SIZE' @@ -270,53 +240,41 @@ for i in range(n_accesses): keys = sorted(access_indices.keys(), reverse=True) # Are the keys used bigger than range_check bound. ids.big_keys = 1 if keys[0] >= range_check_builtin.bound else 0 -ids.first_key = key = keys.pop()"#; - -pub const SQUASH_DICT_INNER_SKIP_LOOP: &str = - "ids.should_skip_loop = 0 if current_access_indices else 1"; -pub const SQUASH_DICT_INNER_FIRST_ITERATION: &str = r#"current_access_indices = sorted(access_indices[key])[::-1] +ids.first_key = key = keys.pop()"#}), +(SQUASH_DICT_INNER_SKIP_LOOP, indoc! {r#"ids.should_skip_loop = 0 if current_access_indices else 1"#}), +(SQUASH_DICT_INNER_FIRST_ITERATION, indoc! {r#"current_access_indices = sorted(access_indices[key])[::-1] current_access_index = current_access_indices.pop() -memory[ids.range_check_ptr] = current_access_index"#; - -pub const SQUASH_DICT_INNER_CHECK_ACCESS_INDEX: &str = r#"new_access_index = current_access_indices.pop() +memory[ids.range_check_ptr] = current_access_index"#}), +(SQUASH_DICT_INNER_CHECK_ACCESS_INDEX, indoc! {r#"new_access_index = current_access_indices.pop() ids.loop_temps.index_delta_minus1 = new_access_index - current_access_index - 1 -current_access_index = new_access_index"#; - -pub const SQUASH_DICT_INNER_CONTINUE_LOOP: &str = - "ids.loop_temps.should_continue = 1 if current_access_indices else 0"; -pub const SQUASH_DICT_INNER_ASSERT_LEN_KEYS: &str = "assert len(keys) == 0"; -pub const SQUASH_DICT_INNER_LEN_ASSERT: &str = "assert len(current_access_indices) == 0"; -pub const SQUASH_DICT_INNER_USED_ACCESSES_ASSERT: &str = - "assert ids.n_used_accesses == len(access_indices[key])"; -pub const SQUASH_DICT_INNER_NEXT_KEY: &str = r#"assert len(keys) > 0, 'No keys left but remaining_accesses > 0.' -ids.next_key = key = keys.pop()"#; - -pub const DICT_SQUASH_COPY_DICT: &str = r#"# Prepare arguments for dict_new. In particular, the same dictionary values should be copied +current_access_index = new_access_index"#}), +(SQUASH_DICT_INNER_CONTINUE_LOOP, indoc! {r#"ids.loop_temps.should_continue = 1 if current_access_indices else 0"#}), +(SQUASH_DICT_INNER_ASSERT_LEN_KEYS, indoc! {r#"assert len(keys) == 0"#}), +(SQUASH_DICT_INNER_LEN_ASSERT, indoc! {r#"assert len(current_access_indices) == 0"#}), +(SQUASH_DICT_INNER_USED_ACCESSES_ASSERT, indoc! {r#"assert ids.n_used_accesses == len(access_indices[key])"#}), +(SQUASH_DICT_INNER_NEXT_KEY, indoc! {r#"assert len(keys) > 0, 'No keys left but remaining_accesses > 0.' +ids.next_key = key = keys.pop()"#}), +(DICT_SQUASH_COPY_DICT, indoc! {r#"# Prepare arguments for dict_new. In particular, the same dictionary values should be copied # to the new (squashed) dictionary. vm_enter_scope({ # Make __dict_manager accessible. '__dict_manager': __dict_manager, # Create a copy of the dict, in case it changes in the future. 'initial_dict': dict(__dict_manager.get_dict(ids.dict_accesses_end)), -})"#; - -pub const DICT_SQUASH_UPDATE_PTR: &str = r#"# Update the DictTracker's current_ptr to point to the end of the squashed dict. +})"#}), +(DICT_SQUASH_UPDATE_PTR, indoc! {r#"# Update the DictTracker's current_ptr to point to the end of the squashed dict. __dict_manager.get_tracker(ids.squashed_dict_start).current_ptr = \ - ids.squashed_dict_end.address_"#; - -pub const BIGINT_TO_UINT256: &str = "ids.low = (ids.x.d0 + ids.x.d1 * ids.BASE) & ((1 << 128) - 1)"; -pub const UINT256_ADD: &str = r#"sum_low = ids.a.low + ids.b.low + ids.squashed_dict_end.address_"#}), +(BIGINT_TO_UINT256, indoc! {r#"ids.low = (ids.x.d0 + ids.x.d1 * ids.BASE) & ((1 << 128) - 1)"#}), +(UINT256_ADD, indoc! {r#"sum_low = ids.a.low + ids.b.low ids.carry_low = 1 if sum_low >= ids.SHIFT else 0 sum_high = ids.a.high + ids.b.high + ids.carry_low -ids.carry_high = 1 if sum_high >= ids.SHIFT else 0"#; - -pub const UINT256_ADD_LOW: &str = r#"sum_low = ids.a.low + ids.b.low -ids.carry_low = 1 if sum_low >= ids.SHIFT else 0"#; - -pub const UINT128_ADD: &str = r#"res = ids.a + ids.b -ids.carry = 1 if res >= ids.SHIFT else 0"#; - -pub const UINT256_SUB: &str = r#"def split(num: int, num_bits_shift: int = 128, length: int = 2): +ids.carry_high = 1 if sum_high >= ids.SHIFT else 0"#}), +(UINT256_ADD_LOW, indoc! {r#"sum_low = ids.a.low + ids.b.low +ids.carry_low = 1 if sum_low >= ids.SHIFT else 0"#}), +(UINT128_ADD, indoc! {r#"res = ids.a + ids.b +ids.carry = 1 if res >= ids.SHIFT else 0"#}), +(UINT256_SUB, indoc! {r#"def split(num: int, num_bits_shift: int = 128, length: int = 2): a = [] for _ in range(length): a.append( num & ((1 << num_bits_shift) - 1) ) @@ -332,42 +290,36 @@ b = pack(ids.b) res = (a - b)%2**256 res_split = split(res) ids.res.low = res_split[0] -ids.res.high = res_split[1]"#; - -pub const UINT256_SQRT: &str = r#"from starkware.python.math_utils import isqrt +ids.res.high = res_split[1]"#}), +(UINT256_SQRT, indoc! {r#"from starkware.python.math_utils import isqrt n = (ids.n.high << 128) + ids.n.low root = isqrt(n) assert 0 <= root < 2 ** 128 ids.root.low = root -ids.root.high = 0"#; - -pub const UINT256_SQRT_FELT: &str = r#"from starkware.python.math_utils import isqrt +ids.root.high = 0"#}), +(UINT256_SQRT_FELT, indoc! {r#"from starkware.python.math_utils import isqrt n = (ids.n.high << 128) + ids.n.low root = isqrt(n) assert 0 <= root < 2 ** 128 -ids.root = root"#; - -pub const UINT256_SIGNED_NN: &str = "memory[ap] = 1 if 0 <= (ids.a.high % PRIME) < 2 ** 127 else 0"; - -pub const UINT256_UNSIGNED_DIV_REM: &str = r#"a = (ids.a.high << 128) + ids.a.low +ids.root = root"#}), +(UINT256_SIGNED_NN, indoc! {r#"memory[ap] = 1 if 0 <= (ids.a.high % PRIME) < 2 ** 127 else 0"#}), +(UINT256_UNSIGNED_DIV_REM, indoc! {r#"a = (ids.a.high << 128) + ids.a.low div = (ids.div.high << 128) + ids.div.low quotient, remainder = divmod(a, div) ids.quotient.low = quotient & ((1 << 128) - 1) ids.quotient.high = quotient >> 128 ids.remainder.low = remainder & ((1 << 128) - 1) -ids.remainder.high = remainder >> 128"#; - -pub const UINT256_EXPANDED_UNSIGNED_DIV_REM: &str = r#"a = (ids.a.high << 128) + ids.a.low +ids.remainder.high = remainder >> 128"#}), +(UINT256_EXPANDED_UNSIGNED_DIV_REM, indoc! {r#"a = (ids.a.high << 128) + ids.a.low div = (ids.div.b23 << 128) + ids.div.b01 quotient, remainder = divmod(a, div) ids.quotient.low = quotient & ((1 << 128) - 1) ids.quotient.high = quotient >> 128 ids.remainder.low = remainder & ((1 << 128) - 1) -ids.remainder.high = remainder >> 128"#; - -pub const UINT256_MUL_DIV_MOD: &str = r#"a = (ids.a.high << 128) + ids.a.low +ids.remainder.high = remainder >> 128"#}), +(UINT256_MUL_DIV_MOD, indoc! {r#"a = (ids.a.high << 128) + ids.a.low b = (ids.b.high << 128) + ids.b.low div = (ids.div.high << 128) + ids.div.low quotient, remainder = divmod(a * b, div) @@ -377,11 +329,9 @@ ids.quotient_low.high = (quotient >> 128) & ((1 << 128) - 1) ids.quotient_high.low = (quotient >> 256) & ((1 << 128) - 1) ids.quotient_high.high = quotient >> 384 ids.remainder.low = remainder & ((1 << 128) - 1) -ids.remainder.high = remainder >> 128"#; - -pub const USORT_ENTER_SCOPE: &str = - "vm_enter_scope(dict(__usort_max_size = globals().get('__usort_max_size')))"; -pub const USORT_BODY: &str = r#"from collections import defaultdict +ids.remainder.high = remainder >> 128"#}), +(USORT_ENTER_SCOPE, indoc! {r#"vm_enter_scope(dict(__usort_max_size = globals().get('__usort_max_size')))"#}), +(USORT_BODY, indoc! {r#"from collections import defaultdict input_ptr = ids.input input_len = int(ids.input_len) @@ -399,20 +349,16 @@ for i in range(input_len): output = sorted(positions_dict.keys()) ids.output_len = len(output) ids.output = segments.gen_arg(output) -ids.multiplicities = segments.gen_arg([len(positions_dict[k]) for k in output])"#; - -pub const USORT_VERIFY: &str = r#"last_pos = 0 -positions = positions_dict[ids.value][::-1]"#; - -pub const USORT_VERIFY_MULTIPLICITY_ASSERT: &str = "assert len(positions) == 0"; -pub const USORT_VERIFY_MULTIPLICITY_BODY: &str = r#"current_pos = positions.pop() +ids.multiplicities = segments.gen_arg([len(positions_dict[k]) for k in output])"#}), +(USORT_VERIFY, indoc! {r#"last_pos = 0 +positions = positions_dict[ids.value][::-1]"#}), +(USORT_VERIFY_MULTIPLICITY_ASSERT, indoc! {r#"assert len(positions) == 0"#}), +(USORT_VERIFY_MULTIPLICITY_BODY, indoc! {r#"current_pos = positions.pop() ids.next_item_index = current_pos - last_pos -last_pos = current_pos + 1"#; - -pub const BLAKE2S_COMPUTE: &str = r#"from starkware.cairo.common.cairo_blake2s.blake2s_utils import compute_blake2s_func -compute_blake2s_func(segments=segments, output_ptr=ids.output)"#; - -pub const BLAKE2S_FINALIZE: &str = r#"# Add dummy pairs of input and output. +last_pos = current_pos + 1"#}), +(BLAKE2S_COMPUTE, indoc! {r#"from starkware.cairo.common.cairo_blake2s.blake2s_utils import compute_blake2s_func +compute_blake2s_func(segments=segments, output_ptr=ids.output)"#}), +(BLAKE2S_FINALIZE, indoc! {r#"# Add dummy pairs of input and output. from starkware.cairo.common.cairo_blake2s.blake2s_utils import IV, blake2s_compress _n_packed_instances = int(ids.N_PACKED_INSTANCES) @@ -431,9 +377,8 @@ output = blake2s_compress( f1=0, ) padding = (modified_iv + message + [0, 0xffffffff] + output) * (_n_packed_instances - 1) -segments.write_arg(ids.blake2s_ptr_end, padding)"#; - -pub const BLAKE2S_FINALIZE_V2: &str = r#"# Add dummy pairs of input and output. +segments.write_arg(ids.blake2s_ptr_end, padding)"#}), +(BLAKE2S_FINALIZE_V2, indoc! {r#"# Add dummy pairs of input and output. from starkware.cairo.common.cairo_blake2s.blake2s_utils import IV, blake2s_compress _n_packed_instances = int(ids.N_PACKED_INSTANCES) @@ -452,9 +397,8 @@ output = blake2s_compress( f1=0, ) padding = (modified_iv + message + [0, 0xffffffff] + output) * (_n_packed_instances - 1) -segments.write_arg(ids.blake2s_ptr_end, padding)"#; - -pub const BLAKE2S_FINALIZE_V3: &str = r#"# Add dummy pairs of input and output. +segments.write_arg(ids.blake2s_ptr_end, padding)"#}), +(BLAKE2S_FINALIZE_V3, indoc! {r#"# Add dummy pairs of input and output. from starkware.cairo.common.cairo_blake2s.blake2s_utils import IV, blake2s_compress _n_packed_instances = int(ids.N_PACKED_INSTANCES) @@ -473,19 +417,16 @@ output = blake2s_compress( f1=0, ) padding = (message + modified_iv + [0, 0xffffffff] + output) * (_n_packed_instances - 1) -segments.write_arg(ids.blake2s_ptr_end, padding)"#; - -pub const BLAKE2S_ADD_UINT256: &str = r#"B = 32 +segments.write_arg(ids.blake2s_ptr_end, padding)"#}), +(BLAKE2S_ADD_UINT256, indoc! {r#"B = 32 MASK = 2 ** 32 - 1 segments.write_arg(ids.data, [(ids.low >> (B * i)) & MASK for i in range(4)]) -segments.write_arg(ids.data + 4, [(ids.high >> (B * i)) & MASK for i in range(4)])"#; - -pub const BLAKE2S_ADD_UINT256_BIGEND: &str = r#"B = 32 +segments.write_arg(ids.data + 4, [(ids.high >> (B * i)) & MASK for i in range(4)])"#}), +(BLAKE2S_ADD_UINT256_BIGEND, indoc! {r#"B = 32 MASK = 2 ** 32 - 1 segments.write_arg(ids.data, [(ids.high >> (B * (3 - i))) & MASK for i in range(4)]) -segments.write_arg(ids.data + 4, [(ids.low >> (B * (3 - i))) & MASK for i in range(4)])"#; - -pub const EXAMPLE_BLAKE2S_COMPRESS: &str = r#"from starkware.cairo.common.cairo_blake2s.blake2s_utils import IV, blake2s_compress +segments.write_arg(ids.data + 4, [(ids.low >> (B * (3 - i))) & MASK for i in range(4)])"#}), +(EXAMPLE_BLAKE2S_COMPRESS, indoc! {r#"from starkware.cairo.common.cairo_blake2s.blake2s_utils import IV, blake2s_compress _blake2s_input_chunk_size_felts = int(ids.BLAKE2S_INPUT_CHUNK_SIZE_FELTS) assert 0 <= _blake2s_input_chunk_size_felts < 100 @@ -499,52 +440,42 @@ new_state = blake2s_compress( f1=0, ) -segments.write_arg(ids.output, new_state)"#; - -pub const NONDET_BIGINT3_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import split - -segments.write_arg(ids.res.address_, split(value))"#; +segments.write_arg(ids.output, new_state)"#}), +(NONDET_BIGINT3_V1, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import split -pub const NONDET_BIGINT3_V2: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import split -segments.write_arg(ids.res.address_, split(value))"#; - -pub const VERIFY_ZERO_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +segments.write_arg(ids.res.address_, split(value))"#}), +(NONDET_BIGINT3_V2, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import split +segments.write_arg(ids.res.address_, split(value))"#}), +(VERIFY_ZERO_V1, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack q, r = divmod(pack(ids.val, PRIME), SECP_P) assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." -ids.q = q % PRIME"#; - -pub const VERIFY_ZERO_V2: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P +ids.q = q % PRIME"#}), +(VERIFY_ZERO_V2, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P q, r = divmod(pack(ids.val, PRIME), SECP_P) assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." -ids.q = q % PRIME"#; - -pub const VERIFY_ZERO_V3: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +ids.q = q % PRIME"#}), +(VERIFY_ZERO_V3, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack SECP_P = 2**255-19 to_assert = pack(ids.val, PRIME) q, r = divmod(pack(ids.val, PRIME), SECP_P) assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." -ids.q = q % PRIME"#; - -pub const VERIFY_ZERO_EXTERNAL_SECP: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +ids.q = q % PRIME"#}), +(VERIFY_ZERO_EXTERNAL_SECP, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack q, r = divmod(pack(ids.val, PRIME), SECP_P) assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." -ids.q = q % PRIME"#; - -pub const REDUCE_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack - -value = pack(ids.x, PRIME) % SECP_P"#; - -pub const REDUCE_V2: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack -value = pack(ids.x, PRIME) % SECP_P"#; +ids.q = q % PRIME"#}), +(REDUCE_V1, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack -pub const REDUCE_ED25519: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +value = pack(ids.x, PRIME) % SECP_P"#}), +(REDUCE_V2, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +value = pack(ids.x, PRIME) % SECP_P"#}), +(REDUCE_ED25519, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack SECP_P=2**255-19 -value = pack(ids.x, PRIME) % SECP_P"#; - -pub const UNSAFE_KECCAK: &str = r#"from eth_hash.auto import keccak +value = pack(ids.x, PRIME) % SECP_P"#}), +(UNSAFE_KECCAK, indoc! {r#"from eth_hash.auto import keccak data, length = ids.data, ids.length @@ -562,72 +493,58 @@ for word_i, byte_i in enumerate(range(0, length, 16)): hashed = keccak(keccak_input) ids.high = int.from_bytes(hashed[:16], 'big') -ids.low = int.from_bytes(hashed[16:32], 'big')"#; - -pub const UNSAFE_KECCAK_FINALIZE: &str = r#"from eth_hash.auto import keccak +ids.low = int.from_bytes(hashed[16:32], 'big')"#}), +(UNSAFE_KECCAK_FINALIZE, indoc! {r#"from eth_hash.auto import keccak keccak_input = bytearray() n_elms = ids.keccak_state.end_ptr - ids.keccak_state.start_ptr for word in memory.get_range(ids.keccak_state.start_ptr, n_elms): keccak_input += word.to_bytes(16, 'big') hashed = keccak(keccak_input) ids.high = int.from_bytes(hashed[:16], 'big') -ids.low = int.from_bytes(hashed[16:32], 'big')"#; - -pub const IS_ZERO_NONDET: &str = "memory[ap] = to_felt_or_relocatable(x == 0)"; -pub const IS_ZERO_INT: &str = "memory[ap] = int(x == 0)"; -pub const IS_ZERO_PACK_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack - -x = pack(ids.x, PRIME) % SECP_P"#; - -pub const IS_ZERO_PACK_V2: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack -x = pack(ids.x, PRIME) % SECP_P"#; - -pub const IS_ZERO_PACK_EXTERNAL_SECP_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack - -x = pack(ids.x, PRIME) % SECP_P"#; - -pub const IS_ZERO_PACK_EXTERNAL_SECP_V2: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack -x = pack(ids.x, PRIME) % SECP_P"#; - -pub const IS_ZERO_PACK_ED25519: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +ids.low = int.from_bytes(hashed[16:32], 'big')"#}), +(IS_ZERO_NONDET, indoc! {r#"memory[ap] = to_felt_or_relocatable(x == 0)"#}), +(IS_ZERO_INT, indoc! {r#"memory[ap] = int(x == 0)"#}), +(IS_ZERO_PACK_V1, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack + +x = pack(ids.x, PRIME) % SECP_P"#}), +(IS_ZERO_PACK_V2, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +x = pack(ids.x, PRIME) % SECP_P"#}), +(IS_ZERO_PACK_EXTERNAL_SECP_V1, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack + +x = pack(ids.x, PRIME) % SECP_P"#}), +(IS_ZERO_PACK_EXTERNAL_SECP_V2, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +x = pack(ids.x, PRIME) % SECP_P"#}), +(IS_ZERO_PACK_ED25519, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack SECP_P=2**255-19 -x = pack(ids.x, PRIME) % SECP_P"#; - -pub const IS_ZERO_ASSIGN_SCOPE_VARS: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P +x = pack(ids.x, PRIME) % SECP_P"#}), +(IS_ZERO_ASSIGN_SCOPE_VARS, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P from starkware.python.math_utils import div_mod -value = x_inv = div_mod(1, x, SECP_P)"#; +value = x_inv = div_mod(1, x, SECP_P)"#}), +(IS_ZERO_ASSIGN_SCOPE_VARS_EXTERNAL_SECP, indoc! {r#"from starkware.python.math_utils import div_mod -pub const IS_ZERO_ASSIGN_SCOPE_VARS_EXTERNAL_SECP: &str = r#"from starkware.python.math_utils import div_mod - -value = x_inv = div_mod(1, x, SECP_P)"#; - -pub const IS_ZERO_ASSIGN_SCOPE_VARS_ED25519: &str = r#"SECP_P=2**255-19 +value = x_inv = div_mod(1, x, SECP_P)"#}), +(IS_ZERO_ASSIGN_SCOPE_VARS_ED25519, indoc! {r#"SECP_P=2**255-19 from starkware.python.math_utils import div_mod -value = x_inv = div_mod(1, x, SECP_P)"#; - -pub const DIV_MOD_N_PACKED_DIVMOD_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import N, pack +value = x_inv = div_mod(1, x, SECP_P)"#}), +(DIV_MOD_N_PACKED_DIVMOD_V1, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import N, pack from starkware.python.math_utils import div_mod, safe_div a = pack(ids.a, PRIME) b = pack(ids.b, PRIME) -value = res = div_mod(a, b, N)"#; - -pub const DIV_MOD_N_PACKED_DIVMOD_EXTERNAL_N: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +value = res = div_mod(a, b, N)"#}), +(DIV_MOD_N_PACKED_DIVMOD_EXTERNAL_N, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.python.math_utils import div_mod, safe_div a = pack(ids.a, PRIME) b = pack(ids.b, PRIME) -value = res = div_mod(a, b, N)"#; - -pub const DIV_MOD_N_SAFE_DIV: &str = r#"value = k = safe_div(res * b - a, N)"#; - -pub const GET_FELT_BIT_LENGTH: &str = r#"x = ids.x -ids.bit_length = x.bit_length()"#; - -pub const BIGINT_PACK_DIV_MOD: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +value = res = div_mod(a, b, N)"#}), +(DIV_MOD_N_SAFE_DIV, indoc! {r#"value = k = safe_div(res * b - a, N)"#}), +(GET_FELT_BIT_LENGTH, indoc! {r#"x = ids.x +ids.bit_length = x.bit_length()"#}), +(BIGINT_PACK_DIV_MOD, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.cairo.common.math_utils import as_int from starkware.python.math_utils import div_mod, safe_div @@ -635,16 +552,12 @@ p = pack(ids.P, PRIME) x = pack(ids.x, PRIME) + as_int(ids.x.d3, PRIME) * ids.BASE ** 3 + as_int(ids.x.d4, PRIME) * ids.BASE ** 4 y = pack(ids.y, PRIME) -value = res = div_mod(x, y, p)"#; - -pub const BIGINT_SAFE_DIV: &str = r#"k = safe_div(res * y - x, p) +value = res = div_mod(x, y, p)"#}), +(BIGINT_SAFE_DIV, indoc! {r#"k = safe_div(res * y - x, p) value = k if k > 0 else 0 - k -ids.flag = 1 if k > 0 else 0"#; - -pub const DIV_MOD_N_SAFE_DIV_PLUS_ONE: &str = - r#"value = k_plus_one = safe_div(res * b - a, N) + 1"#; - -pub const GET_POINT_FROM_X: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +ids.flag = 1 if k > 0 else 0"#}), +(DIV_MOD_N_SAFE_DIV_PLUS_ONE, indoc! {r#"value = k_plus_one = safe_div(res * b - a, N) + 1"#}), +(GET_POINT_FROM_X, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack x_cube_int = pack(ids.x_cube, PRIME) % SECP_P y_square_int = (x_cube_int + ids.BETA) % SECP_P @@ -654,64 +567,56 @@ y = pow(y_square_int, (SECP_P + 1) // 4, SECP_P) if ids.v % 2 == y % 2: value = y else: - value = (-y) % SECP_P"#; - -pub const EC_NEGATE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack + value = (-y) % SECP_P"#}), +(EC_NEGATE, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack y = pack(ids.point.y, PRIME) % SECP_P # The modulo operation in python always returns a nonnegative number. -value = (-y) % SECP_P"#; - -pub const EC_NEGATE_EMBEDDED_SECP: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +value = (-y) % SECP_P"#}), +(EC_NEGATE_EMBEDDED_SECP, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack SECP_P = 2**255-19 y = pack(ids.point.y, PRIME) % SECP_P # The modulo operation in python always returns a nonnegative number. -value = (-y) % SECP_P"#; - -pub const EC_DOUBLE_SLOPE_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +value = (-y) % SECP_P"#}), +(EC_DOUBLE_SLOPE_V1, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack from starkware.python.math_utils import ec_double_slope # Compute the slope. x = pack(ids.point.x, PRIME) y = pack(ids.point.y, PRIME) -value = slope = ec_double_slope(point=(x, y), alpha=0, p=SECP_P)"#; - -pub const EC_DOUBLE_SLOPE_V2: &str = r#"from starkware.python.math_utils import ec_double_slope +value = slope = ec_double_slope(point=(x, y), alpha=0, p=SECP_P)"#}), +(EC_DOUBLE_SLOPE_V2, indoc! {r#"from starkware.python.math_utils import ec_double_slope from starkware.cairo.common.cairo_secp.secp_utils import pack SECP_P = 2**255-19 # Compute the slope. x = pack(ids.point.x, PRIME) y = pack(ids.point.y, PRIME) -value = slope = ec_double_slope(point=(x, y), alpha=42204101795669822316448953119945047945709099015225996174933988943478124189485, p=SECP_P)"#; - -pub const EC_DOUBLE_SLOPE_V3: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +value = slope = ec_double_slope(point=(x, y), alpha=42204101795669822316448953119945047945709099015225996174933988943478124189485, p=SECP_P)"#}), +(EC_DOUBLE_SLOPE_V3, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack from starkware.python.math_utils import div_mod # Compute the slope. x = pack(ids.pt.x, PRIME) y = pack(ids.pt.y, PRIME) -value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P)"#; - -pub const EC_DOUBLE_SLOPE_V4: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA, SECP256R1_P +value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P)"#}), +(EC_DOUBLE_SLOPE_V4, indoc! {r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA, SECP256R1_P from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.python.math_utils import ec_double_slope # Compute the slope. x = pack(ids.point.x, SECP256R1_P) y = pack(ids.point.y, SECP256R1_P) -value = slope = ec_double_slope(point=(x, y), alpha=SECP256R1_ALPHA, p=SECP256R1_P)"#; - -pub const EC_DOUBLE_SLOPE_EXTERNAL_CONSTS: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +value = slope = ec_double_slope(point=(x, y), alpha=SECP256R1_ALPHA, p=SECP256R1_P)"#}), +(EC_DOUBLE_SLOPE_EXTERNAL_CONSTS, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.python.math_utils import ec_double_slope # Compute the slope. x = pack(ids.point.x, PRIME) y = pack(ids.point.y, PRIME) -value = slope = ec_double_slope(point=(x, y), alpha=ALPHA, p=SECP_P)"#; - -pub const COMPUTE_SLOPE_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +value = slope = ec_double_slope(point=(x, y), alpha=ALPHA, p=SECP_P)"#}), +(COMPUTE_SLOPE_V1, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack from starkware.python.math_utils import line_slope # Compute the slope. @@ -719,9 +624,8 @@ x0 = pack(ids.point0.x, PRIME) y0 = pack(ids.point0.y, PRIME) x1 = pack(ids.point1.x, PRIME) y1 = pack(ids.point1.y, PRIME) -value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP_P)"#; - -pub const COMPUTE_SLOPE_V2: &str = r#"from starkware.python.math_utils import line_slope +value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP_P)"#}), +(COMPUTE_SLOPE_V2, indoc! {r#"from starkware.python.math_utils import line_slope from starkware.cairo.common.cairo_secp.secp_utils import pack SECP_P = 2**255-19 # Compute the slope. @@ -729,9 +633,8 @@ x0 = pack(ids.point0.x, PRIME) y0 = pack(ids.point0.y, PRIME) x1 = pack(ids.point1.x, PRIME) y1 = pack(ids.point1.y, PRIME) -value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP_P)"#; - -pub const COMPUTE_SLOPE_SECP256R1_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP_P)"#}), +(COMPUTE_SLOPE_SECP256R1_V1, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.python.math_utils import line_slope # Compute the slope. @@ -739,9 +642,8 @@ x0 = pack(ids.point0.x, PRIME) y0 = pack(ids.point0.y, PRIME) x1 = pack(ids.point1.x, PRIME) y1 = pack(ids.point1.y, PRIME) -value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP_P)"#; - -pub const COMPUTE_SLOPE_SECP256R1_V2: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP_P)"#}), +(COMPUTE_SLOPE_SECP256R1_V2, indoc! {r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.python.math_utils import line_slope @@ -750,12 +652,9 @@ x0 = pack(ids.point0.x, PRIME) y0 = pack(ids.point0.y, PRIME) x1 = pack(ids.point1.x, PRIME) y1 = pack(ids.point1.y, PRIME) -value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP256R1_P)"#; - -pub const IMPORT_SECP256R1_P: &str = - "from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P as SECP_P"; - -pub const COMPUTE_SLOPE_WHITELIST: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP256R1_P)"#}), +(IMPORT_SECP256R1_P, indoc! {r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P as SECP_P"#}), +(COMPUTE_SLOPE_WHITELIST, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack from starkware.python.math_utils import div_mod # Compute the slope. @@ -763,46 +662,39 @@ x0 = pack(ids.pt0.x, PRIME) y0 = pack(ids.pt0.y, PRIME) x1 = pack(ids.pt1.x, PRIME) y1 = pack(ids.pt1.y, PRIME) -value = slope = div_mod(y0 - y1, x0 - x1, SECP_P)"#; - -pub const EC_DOUBLE_ASSIGN_NEW_X_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +value = slope = div_mod(y0 - y1, x0 - x1, SECP_P)"#}), +(EC_DOUBLE_ASSIGN_NEW_X_V1, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack slope = pack(ids.slope, PRIME) x = pack(ids.point.x, PRIME) y = pack(ids.point.y, PRIME) -value = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P"#; - -pub const EC_DOUBLE_ASSIGN_NEW_X_V2: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +value = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P"#}), +(EC_DOUBLE_ASSIGN_NEW_X_V2, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack slope = pack(ids.slope, PRIME) x = pack(ids.point.x, PRIME) y = pack(ids.point.y, PRIME) -value = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P"#; - -pub const EC_DOUBLE_ASSIGN_NEW_X_V3: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +value = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P"#}), +(EC_DOUBLE_ASSIGN_NEW_X_V3, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack SECP_P = 2**255-19 slope = pack(ids.slope, PRIME) x = pack(ids.point.x, PRIME) y = pack(ids.point.y, PRIME) -value = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P"#; - -pub const EC_DOUBLE_ASSIGN_NEW_X_V4: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +value = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P"#}), +(EC_DOUBLE_ASSIGN_NEW_X_V4, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack slope = pack(ids.slope, PRIME) x = pack(ids.pt.x, PRIME) y = pack(ids.pt.y, PRIME) -value = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P"#; - -pub const EC_DOUBLE_ASSIGN_NEW_Y: &str = r#"value = new_y = (slope * (x - new_x) - y) % SECP_P"#; - -pub const SHA256_INPUT: &str = r#"ids.full_word = int(ids.n_bytes >= 4)"#; - -pub const SHA256_MAIN_CONSTANT_INPUT_LENGTH: &str = r#"from starkware.cairo.common.cairo_sha256.sha256_utils import ( +value = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P"#}), +(EC_DOUBLE_ASSIGN_NEW_Y, indoc! {r#"value = new_y = (slope * (x - new_x) - y) % SECP_P"#}), +(SHA256_INPUT, indoc! {r#"ids.full_word = int(ids.n_bytes >= 4)"#}), +(SHA256_MAIN_CONSTANT_INPUT_LENGTH, indoc! {r#"from starkware.cairo.common.cairo_sha256.sha256_utils import ( IV, compute_message_schedule, sha2_compress_function) _sha256_input_chunk_size_felts = int(ids.SHA256_INPUT_CHUNK_SIZE_FELTS) @@ -811,9 +703,8 @@ assert 0 <= _sha256_input_chunk_size_felts < 100 w = compute_message_schedule(memory.get_range( ids.sha256_start, _sha256_input_chunk_size_felts)) new_state = sha2_compress_function(IV, w) -segments.write_arg(ids.output, new_state)"#; - -pub const SHA256_MAIN_ARBITRARY_INPUT_LENGTH: &str = r#"from starkware.cairo.common.cairo_sha256.sha256_utils import ( +segments.write_arg(ids.output, new_state)"#}), +(SHA256_MAIN_ARBITRARY_INPUT_LENGTH, indoc! {r#"from starkware.cairo.common.cairo_sha256.sha256_utils import ( compute_message_schedule, sha2_compress_function) _sha256_input_chunk_size_felts = int(ids.SHA256_INPUT_CHUNK_SIZE_FELTS) @@ -823,9 +714,8 @@ assert 0 <= _sha256_state_size_felts < 100 w = compute_message_schedule(memory.get_range( ids.sha256_start, _sha256_input_chunk_size_felts)) new_state = sha2_compress_function(memory.get_range(ids.state, _sha256_state_size_felts), w) -segments.write_arg(ids.output, new_state)"#; - -pub const SHA256_FINALIZE: &str = r#"# Add dummy pairs of input and output. +segments.write_arg(ids.output, new_state)"#}), +(SHA256_FINALIZE, indoc! {r#"# Add dummy pairs of input and output. from starkware.cairo.common.cairo_sha256.sha256_utils import ( IV, compute_message_schedule, sha2_compress_function) @@ -838,72 +728,59 @@ message = [0] * _sha256_input_chunk_size_felts w = compute_message_schedule(message) output = sha2_compress_function(IV, w) padding = (message + IV + output) * (_block_size - 1) -segments.write_arg(ids.sha256_ptr_end, padding)"#; - -pub const KECCAK_WRITE_ARGS: &str = r#"segments.write_arg(ids.inputs, [ids.low % 2 ** 64, ids.low // 2 ** 64]) -segments.write_arg(ids.inputs + 2, [ids.high % 2 ** 64, ids.high // 2 ** 64])"#; - -pub const COMPARE_BYTES_IN_WORD_NONDET: &str = - r#"memory[ap] = to_felt_or_relocatable(ids.n_bytes < ids.BYTES_IN_WORD)"#; - -pub const COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET: &str = - r#"memory[ap] = to_felt_or_relocatable(ids.n_bytes >= ids.KECCAK_FULL_RATE_IN_BYTES)"#; - -pub const BLOCK_PERMUTATION: &str = r#"from starkware.cairo.common.keccak_utils.keccak_utils import keccak_func +segments.write_arg(ids.sha256_ptr_end, padding)"#}), +(KECCAK_WRITE_ARGS, indoc! {r#"segments.write_arg(ids.inputs, [ids.low % 2 ** 64, ids.low // 2 ** 64]) +segments.write_arg(ids.inputs + 2, [ids.high % 2 ** 64, ids.high // 2 ** 64])"#}), +(COMPARE_BYTES_IN_WORD_NONDET, indoc! {r#"memory[ap] = to_felt_or_relocatable(ids.n_bytes < ids.BYTES_IN_WORD)"#}), +(COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET, indoc! {r#"memory[ap] = to_felt_or_relocatable(ids.n_bytes >= ids.KECCAK_FULL_RATE_IN_BYTES)"#}), +(BLOCK_PERMUTATION, indoc! {r#"from starkware.cairo.common.keccak_utils.keccak_utils import keccak_func _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) assert 0 <= _keccak_state_size_felts < 100 output_values = keccak_func(memory.get_range( ids.keccak_ptr - _keccak_state_size_felts, _keccak_state_size_felts)) -segments.write_arg(ids.keccak_ptr, output_values)"#; - +segments.write_arg(ids.keccak_ptr, output_values)"#}), // The 0.10.3 whitelist uses this variant (instead of the one used by the common library), but both hints have the same behaviour // We should check for future refactors that may discard one of the variants -pub const BLOCK_PERMUTATION_WHITELIST_V1: &str = r#"from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func +(BLOCK_PERMUTATION_WHITELIST_V1, indoc! {r#"from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) assert 0 <= _keccak_state_size_felts < 100 output_values = keccak_func(memory.get_range( ids.keccak_ptr - _keccak_state_size_felts, _keccak_state_size_felts)) -segments.write_arg(ids.keccak_ptr, output_values)"#; - -pub const BLOCK_PERMUTATION_WHITELIST_V2: &str = r#"from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func +segments.write_arg(ids.keccak_ptr, output_values)"#}), +(BLOCK_PERMUTATION_WHITELIST_V2, indoc! {r#"from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) assert 0 <= _keccak_state_size_felts < 100 output_values = keccak_func(memory.get_range( ids.keccak_ptr_start, _keccak_state_size_felts)) -segments.write_arg(ids.output, output_values)"#; - -pub const CAIRO_KECCAK_INPUT_IS_FULL_WORD: &str = r#"ids.full_word = int(ids.n_bytes >= 8)"#; - -pub const CAIRO_KECCAK_FINALIZE_V1: &str = r#"# Add dummy pairs of input and output. +segments.write_arg(ids.output, output_values)"#}), +(CAIRO_KECCAK_INPUT_IS_FULL_WORD, indoc! {r#"ids.full_word = int(ids.n_bytes >= 8)"#}), +(CAIRO_KECCAK_FINALIZE_V1, indoc! {r#"# Add dummy pairs of input and output. _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) _block_size = int(ids.BLOCK_SIZE) assert 0 <= _keccak_state_size_felts < 100 assert 0 <= _block_size < 10 inp = [0] * _keccak_state_size_felts padding = (inp + keccak_func(inp)) * _block_size -segments.write_arg(ids.keccak_ptr_end, padding)"#; - -pub const CAIRO_KECCAK_FINALIZE_V2: &str = r#"# Add dummy pairs of input and output. +segments.write_arg(ids.keccak_ptr_end, padding)"#}), +(CAIRO_KECCAK_FINALIZE_V2, indoc! {r#"# Add dummy pairs of input and output. _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) _block_size = int(ids.BLOCK_SIZE) assert 0 <= _keccak_state_size_felts < 100 assert 0 <= _block_size < 1000 inp = [0] * _keccak_state_size_felts padding = (inp + keccak_func(inp)) * _block_size -segments.write_arg(ids.keccak_ptr_end, padding)"#; - -pub const FAST_EC_ADD_ASSIGN_NEW_X: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +segments.write_arg(ids.keccak_ptr_end, padding)"#}), +(FAST_EC_ADD_ASSIGN_NEW_X, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack slope = pack(ids.slope, PRIME) x0 = pack(ids.point0.x, PRIME) x1 = pack(ids.point1.x, PRIME) y0 = pack(ids.point0.y, PRIME) -value = new_x = (pow(slope, 2, SECP_P) - x0 - x1) % SECP_P"#; - -pub const FAST_EC_ADD_ASSIGN_NEW_X_V2: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +value = new_x = (pow(slope, 2, SECP_P) - x0 - x1) % SECP_P"#}), +(FAST_EC_ADD_ASSIGN_NEW_X_V2, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack SECP_P = 2**255-19 slope = pack(ids.slope, PRIME) @@ -911,50 +788,35 @@ x0 = pack(ids.point0.x, PRIME) x1 = pack(ids.point1.x, PRIME) y0 = pack(ids.point0.y, PRIME) -value = new_x = (pow(slope, 2, SECP_P) - x0 - x1) % SECP_P"#; - -pub const FAST_EC_ADD_ASSIGN_NEW_X_V3: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +value = new_x = (pow(slope, 2, SECP_P) - x0 - x1) % SECP_P"#}), +(FAST_EC_ADD_ASSIGN_NEW_X_V3, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack slope = pack(ids.slope, PRIME) x0 = pack(ids.pt0.x, PRIME) x1 = pack(ids.pt1.x, PRIME) y0 = pack(ids.pt0.y, PRIME) -value = new_x = (pow(slope, 2, SECP_P) - x0 - x1) % SECP_P"#; - -pub const FAST_EC_ADD_ASSIGN_NEW_Y: &str = - r#"value = new_y = (slope * (x0 - new_x) - y0) % SECP_P"#; - -pub const EC_MUL_INNER: &str = r#"memory[ap] = (ids.scalar % PRIME) % 2"#; - -pub const RELOCATE_SEGMENT: &str = - r#"memory.add_relocation_rule(src_ptr=ids.src_ptr, dest_ptr=ids.dest_ptr)"#; - -pub const TEMPORARY_ARRAY: &str = r#"ids.temporary_array = segments.add_temp_segment()"#; -pub const VERIFY_ECDSA_SIGNATURE: &str = - r#"ecdsa_builtin.add_signature(ids.ecdsa_ptr.address_, (ids.signature_r, ids.signature_s))"#; - -pub const SPLIT_OUTPUT_0: &str = "ids.output0_low = ids.output0 & ((1 << 128) - 1) -ids.output0_high = ids.output0 >> 128"; -pub const SPLIT_OUTPUT_1: &str = "ids.output1_low = ids.output1 & ((1 << 128) - 1) -ids.output1_high = ids.output1 >> 128"; - -pub const SPLIT_INPUT_3: &str = "ids.high3, ids.low3 = divmod(memory[ids.inputs + 3], 256)"; -pub const SPLIT_INPUT_6: &str = "ids.high6, ids.low6 = divmod(memory[ids.inputs + 6], 256 ** 2)"; -pub const SPLIT_INPUT_9: &str = "ids.high9, ids.low9 = divmod(memory[ids.inputs + 9], 256 ** 3)"; -pub const SPLIT_INPUT_12: &str = - "ids.high12, ids.low12 = divmod(memory[ids.inputs + 12], 256 ** 4)"; -pub const SPLIT_INPUT_15: &str = - "ids.high15, ids.low15 = divmod(memory[ids.inputs + 15], 256 ** 5)"; - -pub const SPLIT_N_BYTES: &str = - "ids.n_words_to_copy, ids.n_bytes_left = divmod(ids.n_bytes, ids.BYTES_IN_WORD)"; -pub const SPLIT_OUTPUT_MID_LOW_HIGH: &str = "tmp, ids.output1_low = divmod(ids.output1, 256 ** 7) -ids.output1_high, ids.output1_mid = divmod(tmp, 2 ** 128)"; - -pub const NONDET_N_GREATER_THAN_10: &str = "memory[ap] = to_felt_or_relocatable(ids.n >= 10)"; -pub const NONDET_N_GREATER_THAN_2: &str = "memory[ap] = to_felt_or_relocatable(ids.n >= 2)"; -pub const RANDOM_EC_POINT: &str = r#"from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME +value = new_x = (pow(slope, 2, SECP_P) - x0 - x1) % SECP_P"#}), +(FAST_EC_ADD_ASSIGN_NEW_Y, indoc! {r#"value = new_y = (slope * (x0 - new_x) - y0) % SECP_P"#}), +(EC_MUL_INNER, indoc! {r#"memory[ap] = (ids.scalar % PRIME) % 2"#}), +(RELOCATE_SEGMENT, indoc! {r#"memory.add_relocation_rule(src_ptr=ids.src_ptr, dest_ptr=ids.dest_ptr)"#}), +(TEMPORARY_ARRAY, indoc! {r#"ids.temporary_array = segments.add_temp_segment()"#}), +(VERIFY_ECDSA_SIGNATURE, indoc! {r#"ecdsa_builtin.add_signature(ids.ecdsa_ptr.address_, (ids.signature_r, ids.signature_s))"#}), +(SPLIT_OUTPUT_0, indoc! {r#"ids.output0_low = ids.output0 & ((1 << 128) - 1) +ids.output0_high = ids.output0 >> 128"#}), +(SPLIT_OUTPUT_1, indoc! {r#"ids.output1_low = ids.output1 & ((1 << 128) - 1) +ids.output1_high = ids.output1 >> 128"#}), +(SPLIT_INPUT_3, indoc! {r#"ids.high3, ids.low3 = divmod(memory[ids.inputs + 3], 256)"#}), +(SPLIT_INPUT_6, indoc! {r#"ids.high6, ids.low6 = divmod(memory[ids.inputs + 6], 256 ** 2)"#}), +(SPLIT_INPUT_9, indoc! {r#"ids.high9, ids.low9 = divmod(memory[ids.inputs + 9], 256 ** 3)"#}), +(SPLIT_INPUT_12, indoc! {r#"ids.high12, ids.low12 = divmod(memory[ids.inputs + 12], 256 ** 4)"#}), +(SPLIT_INPUT_15, indoc! {r#"ids.high15, ids.low15 = divmod(memory[ids.inputs + 15], 256 ** 5)"#}), +(SPLIT_N_BYTES, indoc! {r#"ids.n_words_to_copy, ids.n_bytes_left = divmod(ids.n_bytes, ids.BYTES_IN_WORD)"#}), +(SPLIT_OUTPUT_MID_LOW_HIGH, indoc! {r#"tmp, ids.output1_low = divmod(ids.output1, 256 ** 7) +ids.output1_high, ids.output1_mid = divmod(tmp, 2 ** 128)"#}), +(NONDET_N_GREATER_THAN_10, indoc! {r#"memory[ap] = to_felt_or_relocatable(ids.n >= 10)"#}), +(NONDET_N_GREATER_THAN_2, indoc! {r#"memory[ap] = to_felt_or_relocatable(ids.n >= 2)"#}), +(RANDOM_EC_POINT, indoc! {r#"from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME from starkware.python.math_utils import random_ec_point from starkware.python.utils import to_bytes @@ -962,8 +824,8 @@ from starkware.python.utils import to_bytes # (1) The added point s is deterministic. # (2) It's hard to choose inputs for which the builtin will fail. seed = b"".join(map(to_bytes, [ids.p.x, ids.p.y, ids.m, ids.q.x, ids.q.y])) -ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed)"#; -pub const CHAINED_EC_OP_RANDOM_EC_POINT: &str = r#"from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME +ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed)"#}), +(CHAINED_EC_OP_RANDOM_EC_POINT, indoc! {r#"from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME from starkware.python.math_utils import random_ec_point from starkware.python.utils import to_bytes @@ -989,24 +851,22 @@ seed = b"".join( ], ) ) -ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed)"#; -pub const RECOVER_Y: &str = - "from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME +ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed)"#}), +(RECOVER_Y, indoc! {r#"from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME from starkware.python.math_utils import recover_y ids.p.x = ids.x # This raises an exception if `x` is not on the curve. -ids.p.y = recover_y(ids.x, ALPHA, BETA, FIELD_PRIME)"; -pub const PACK_MODN_DIV_MODN: &str = "from starkware.cairo.common.cairo_secp.secp_utils import pack +ids.p.y = recover_y(ids.x, ALPHA, BETA, FIELD_PRIME)"#}), +(PACK_MODN_DIV_MODN, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.python.math_utils import div_mod, safe_div N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 x = pack(ids.x, PRIME) % N s = pack(ids.s, PRIME) % N -value = res = div_mod(x, s, N)"; -pub const XS_SAFE_DIV: &str = "value = k = safe_div(res * s - x, N)"; - +value = res = div_mod(x, s, N)"#}), +(XS_SAFE_DIV, indoc! {r#"value = k = safe_div(res * s - x, N)"#}), // The following hints support the lib https://github.com/NethermindEth/research-basic-Cairo-operations-big-integers/blob/main/lib -pub const UINT384_UNSIGNED_DIV_REM: &str = "def split(num: int, num_bits_shift: int, length: int): +(UINT384_UNSIGNED_DIV_REM, indoc! {r#"def split(num: int, num_bits_shift: int, length: int): a = [] for _ in range(length): a.append( num & ((1 << num_bits_shift) - 1) ) @@ -1031,16 +891,16 @@ ids.quotient.d2 = quotient_split[2] remainder_split = split(remainder, num_bits_shift=128, length=3) ids.remainder.d0 = remainder_split[0] ids.remainder.d1 = remainder_split[1] -ids.remainder.d2 = remainder_split[2]"; -pub const UINT384_SPLIT_128: &str = "ids.low = ids.a & ((1<<128) - 1) -ids.high = ids.a >> 128"; -pub const ADD_NO_UINT384_CHECK: &str = "sum_d0 = ids.a.d0 + ids.b.d0 +ids.remainder.d2 = remainder_split[2]"#}), +(UINT384_SPLIT_128, indoc! {r#"ids.low = ids.a & ((1<<128) - 1) +ids.high = ids.a >> 128"#}), +(ADD_NO_UINT384_CHECK, indoc! {r#"sum_d0 = ids.a.d0 + ids.b.d0 ids.carry_d0 = 1 if sum_d0 >= ids.SHIFT else 0 sum_d1 = ids.a.d1 + ids.b.d1 + ids.carry_d0 ids.carry_d1 = 1 if sum_d1 >= ids.SHIFT else 0 sum_d2 = ids.a.d2 + ids.b.d2 + ids.carry_d1 -ids.carry_d2 = 1 if sum_d2 >= ids.SHIFT else 0"; -pub const UINT384_SQRT: &str = "from starkware.python.math_utils import isqrt +ids.carry_d2 = 1 if sum_d2 >= ids.SHIFT else 0"#}), +(UINT384_SQRT, indoc! {r#"from starkware.python.math_utils import isqrt def split(num: int, num_bits_shift: int, length: int): a = [] @@ -1059,10 +919,8 @@ assert 0 <= root < 2 ** 192 root_split = split(root, num_bits_shift=128, length=3) ids.root.d0 = root_split[0] ids.root.d1 = root_split[1] -ids.root.d2 = root_split[2]"; - -pub const SUB_REDUCED_A_AND_REDUCED_B: &str = - "def split(num: int, num_bits_shift: int, length: int): +ids.root.d2 = root_split[2]"#}), +(SUB_REDUCED_A_AND_REDUCED_B, indoc! {r#"def split(num: int, num_bits_shift: int, length: int): a = [] for _ in range(length): a.append( num & ((1 << num_bits_shift) - 1) ) @@ -1084,10 +942,8 @@ res_split = split(res, num_bits_shift=128, length=3) ids.res.d0 = res_split[0] ids.res.d1 = res_split[1] -ids.res.d2 = res_split[2]"; - -pub const UNSIGNED_DIV_REM_UINT768_BY_UINT384: &str = - "def split(num: int, num_bits_shift: int, length: int): +ids.res.d2 = res_split[2]"#}), +(UNSIGNED_DIV_REM_UINT768_BY_UINT384, indoc! {r#"def split(num: int, num_bits_shift: int, length: int): a = [] for _ in range(length): a.append( num & ((1 << num_bits_shift) - 1) ) @@ -1119,11 +975,10 @@ ids.quotient.d5 = quotient_split[5] remainder_split = split(remainder, num_bits_shift=128, length=3) ids.remainder.d0 = remainder_split[0] ids.remainder.d1 = remainder_split[1] -ids.remainder.d2 = remainder_split[2]"; - +ids.remainder.d2 = remainder_split[2]"#}), // equal to UNSIGNED_DIV_REM_UINT768_BY_UINT384 but with some whitespace removed // in the `num = num >> num_bits_shift` and between `pack` and `pack_extended` -pub const UNSIGNED_DIV_REM_UINT768_BY_UINT384_STRIPPED: &str = r#"def split(num: int, num_bits_shift: int, length: int): +(UNSIGNED_DIV_REM_UINT768_BY_UINT384_STRIPPED, indoc! {r#"def split(num: int, num_bits_shift: int, length: int): a = [] for _ in range(length): a.append( num & ((1 << num_bits_shift) - 1) ) @@ -1155,18 +1010,11 @@ ids.quotient.d5 = quotient_split[5] remainder_split = split(remainder, num_bits_shift=128, length=3) ids.remainder.d0 = remainder_split[0] ids.remainder.d1 = remainder_split[1] -ids.remainder.d2 = remainder_split[2]"#; - -pub const UINT384_SIGNED_NN: &str = "memory[ap] = 1 if 0 <= (ids.a.d2 % PRIME) < 2 ** 127 else 0"; - -pub const IMPORT_SECP256R1_ALPHA: &str = - "from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA as ALPHA"; - -pub const IMPORT_SECP256R1_N: &str = - "from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_N as N"; - -pub const UINT384_GET_SQUARE_ROOT: &str = - "from starkware.python.math_utils import is_quad_residue, sqrt +ids.remainder.d2 = remainder_split[2]"#}), +(UINT384_SIGNED_NN, indoc! {r#"memory[ap] = 1 if 0 <= (ids.a.d2 % PRIME) < 2 ** 127 else 0"#}), +(IMPORT_SECP256R1_ALPHA, indoc! {r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA as ALPHA"#}), +(IMPORT_SECP256R1_N, indoc! {r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_N as N"#}), +(UINT384_GET_SQUARE_ROOT, indoc! {r#"from starkware.python.math_utils import is_quad_residue, sqrt def split(num: int, num_bits_shift: int = 128, length: int = 3): a = [] @@ -1208,9 +1056,8 @@ ids.sqrt_x.d1 = split_root_x[1] ids.sqrt_x.d2 = split_root_x[2] ids.sqrt_gx.d0 = split_root_gx[0] ids.sqrt_gx.d1 = split_root_gx[1] -ids.sqrt_gx.d2 = split_root_gx[2]"; - -pub const UINT256_GET_SQUARE_ROOT: &str = r#"from starkware.python.math_utils import is_quad_residue, sqrt +ids.sqrt_gx.d2 = split_root_gx[2]"#}), +(UINT256_GET_SQUARE_ROOT, indoc! {r#"from starkware.python.math_utils import is_quad_residue, sqrt def split(a: int): return (a & ((1 << 128) - 1), a >> 128) @@ -1244,9 +1091,8 @@ split_root_gx = split(root_gx) ids.sqrt_x.low = split_root_x[0] ids.sqrt_x.high = split_root_x[1] ids.sqrt_gx.low = split_root_gx[0] -ids.sqrt_gx.high = split_root_gx[1]"#; - -pub const UINT384_DIV: &str = "from starkware.python.math_utils import div_mod +ids.sqrt_gx.high = split_root_gx[1]"#}), +(UINT384_DIV, indoc! {r#"from starkware.python.math_utils import div_mod def split(num: int, num_bits_shift: int, length: int): a = [] @@ -1272,9 +1118,8 @@ b_inverse_mod_p_split = split(b_inverse_mod_p, num_bits_shift=128, length=3) ids.b_inverse_mod_p.d0 = b_inverse_mod_p_split[0] ids.b_inverse_mod_p.d1 = b_inverse_mod_p_split[1] -ids.b_inverse_mod_p.d2 = b_inverse_mod_p_split[2]"; - -pub const INV_MOD_P_UINT256: &str = r#"from starkware.python.math_utils import div_mod +ids.b_inverse_mod_p.d2 = b_inverse_mod_p_split[2]"#}), +(INV_MOD_P_UINT256, indoc! {r#"from starkware.python.math_utils import div_mod def split(a: int): return (a & ((1 << 128) - 1), a >> 128) @@ -1294,19 +1139,15 @@ b_inverse_mod_p = div_mod(1, b, p) b_inverse_mod_p_split = split(b_inverse_mod_p) ids.b_inverse_mod_p.low = b_inverse_mod_p_split[0] -ids.b_inverse_mod_p.high = b_inverse_mod_p_split[1]"#; - -pub const HI_MAX_BITLEN: &str = - "ids.len_hi = max(ids.scalar_u.d2.bit_length(), ids.scalar_v.d2.bit_length())-1"; - -pub const QUAD_BIT: &str = r#"ids.quad_bit = ( +ids.b_inverse_mod_p.high = b_inverse_mod_p_split[1]"#}), +(HI_MAX_BITLEN, indoc! {r#"ids.len_hi = max(ids.scalar_u.d2.bit_length(), ids.scalar_v.d2.bit_length())-1"#}), +(QUAD_BIT, indoc! {r#"ids.quad_bit = ( 8 * ((ids.scalar_v >> ids.m) & 1) + 4 * ((ids.scalar_u >> ids.m) & 1) + 2 * ((ids.scalar_v >> (ids.m - 1)) & 1) + ((ids.scalar_u >> (ids.m - 1)) & 1) -)"#; - -pub const INV_MOD_P_UINT512: &str = "def pack_512(u, num_bits_shift: int) -> int: +)"#}), +(INV_MOD_P_UINT512, indoc! {r#"def pack_512(u, num_bits_shift: int) -> int: limbs = (u.d0, u.d1, u.d2, u.d3) return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) @@ -1317,20 +1158,16 @@ x_inverse_mod_p = pow(x,-1, p) x_inverse_mod_p_split = (x_inverse_mod_p & ((1 << 128) - 1), x_inverse_mod_p >> 128) ids.x_inverse_mod_p.low = x_inverse_mod_p_split[0] -ids.x_inverse_mod_p.high = x_inverse_mod_p_split[1]"; - -pub const DI_BIT: &str = - r#"ids.dibit = ((ids.scalar_u >> ids.m) & 1) + 2 * ((ids.scalar_v >> ids.m) & 1)"#; - -pub const EC_RECOVER_DIV_MOD_N_PACKED: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +ids.x_inverse_mod_p.high = x_inverse_mod_p_split[1]"#}), +(DI_BIT, indoc! {r#"ids.dibit = ((ids.scalar_u >> ids.m) & 1) + 2 * ((ids.scalar_v >> ids.m) & 1)"#}), +(EC_RECOVER_DIV_MOD_N_PACKED, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.python.math_utils import div_mod, safe_div N = pack(ids.n, PRIME) x = pack(ids.x, PRIME) % N s = pack(ids.s, PRIME) % N -value = res = div_mod(x, s, N)"#; - -pub const UINT512_UNSIGNED_DIV_REM: &str = r#"def split(num: int, num_bits_shift: int, length: int): +value = res = div_mod(x, s, N)"#}), +(UINT512_UNSIGNED_DIV_REM, indoc! {r#"def split(num: int, num_bits_shift: int, length: int): a = [] for _ in range(length): a.append( num & ((1 << num_bits_shift) - 1) ) @@ -1359,20 +1196,17 @@ ids.quotient.d3 = quotient_split[3] remainder_split = split(remainder, num_bits_shift=128, length=2) ids.remainder.low = remainder_split[0] -ids.remainder.high = remainder_split[1]"#; - -pub const EC_RECOVER_SUB_A_B: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +ids.remainder.high = remainder_split[1]"#}), +(EC_RECOVER_SUB_A_B, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.python.math_utils import div_mod, safe_div a = pack(ids.a, PRIME) b = pack(ids.b, PRIME) -value = res = a - b"#; - -pub const A_B_BITAND_1: &str = "ids.a_lsb = ids.a & 1 -ids.b_lsb = ids.b & 1"; - -pub const EC_RECOVER_PRODUCT_MOD: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +value = res = a - b"#}), +(A_B_BITAND_1, indoc! {r#"ids.a_lsb = ids.a & 1 +ids.b_lsb = ids.b & 1"#}), +(EC_RECOVER_PRODUCT_MOD, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.python.math_utils import div_mod, safe_div a = pack(ids.a, PRIME) @@ -1380,9 +1214,8 @@ b = pack(ids.b, PRIME) product = a * b m = pack(ids.m, PRIME) -value = res = product % m"#; - -pub const UINT256_MUL_INV_MOD_P: &str = r#"from starkware.python.math_utils import div_mod +value = res = product % m"#}), +(UINT256_MUL_INV_MOD_P, indoc! {r#"from starkware.python.math_utils import div_mod def split(a: int): return (a & ((1 << 128) - 1), a >> 128) @@ -1402,21 +1235,17 @@ b_inverse_mod_p = div_mod(1, b, p) b_inverse_mod_p_split = split(b_inverse_mod_p) ids.b_inverse_mod_p.low = b_inverse_mod_p_split[0] -ids.b_inverse_mod_p.high = b_inverse_mod_p_split[1]"#; - -pub const EC_RECOVER_PRODUCT_DIV_M: &str = "value = k = product // m"; - -pub const SQUARE_SLOPE_X_MOD_P: &str = - "from starkware.cairo.common.cairo_secp.secp_utils import pack +ids.b_inverse_mod_p.high = b_inverse_mod_p_split[1]"#}), +(EC_RECOVER_PRODUCT_DIV_M, indoc! {r#"value = k = product // m"#}), +(SQUARE_SLOPE_X_MOD_P, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import pack slope = pack(ids.slope, PRIME) x0 = pack(ids.point0.x, PRIME) x1 = pack(ids.point1.x, PRIME) y0 = pack(ids.point0.y, PRIME) -value = new_x = (pow(slope, 2, SECP_P) - x0 - x1) % SECP_P"; - -pub const SPLIT_XX: &str = "PRIME = 2**255 - 19 +value = new_x = (pow(slope, 2, SECP_P) - x0 - x1) % SECP_P"#}), +(SPLIT_XX, indoc! {r#"PRIME = 2**255 - 19 II = pow(2, (PRIME - 1) // 4, PRIME) xx = ids.xx.low + (ids.xx.high<<128) @@ -1426,39 +1255,27 @@ if (x * x - xx) % PRIME != 0: if x % 2 != 0: x = PRIME - x ids.x.low = x & ((1<<128)-1) -ids.x.high = x >> 128"; -#[cfg(feature = "test_utils")] -pub const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; - -#[cfg(feature = "test_utils")] -pub const PRINT_FELT: &str = "print(ids.x)"; - -#[cfg(feature = "test_utils")] -pub const PRINT_ARR: &str = r#"print(bytes.fromhex(f"{ids.name:062x}").decode().replace('\x00','')) +ids.x.high = x >> 128"#}), +(SKIP_NEXT_INSTRUCTION, indoc! {r#"skip_next_instruction()"#}, "test_utils"), +(PRINT_FELT, indoc! {r#"print(ids.x)"#}, "test_utils"), +(PRINT_ARR, indoc! {r#"print(bytes.fromhex(f"{ids.name:062x}").decode().replace('\x00','')) arr = [memory[ids.arr + i] for i in range(ids.arr_len)] -print(arr)"#; - -#[cfg(feature = "test_utils")] -pub const PRINT_DICT: &str = r#"print(bytes.fromhex(f"{ids.name:062x}").decode().replace('\x00','')) +print(arr)"#}, "test_utils"), +(PRINT_DICT, indoc! {r#"print(bytes.fromhex(f"{ids.name:062x}").decode().replace('\x00','')) data = __dict_manager.get_dict(ids.dict_ptr) print( {k: v if isinstance(v, int) else [memory[v + i] for i in range(ids.pointer_size)] for k, v in data.items()} -)"#; - -pub const RUN_P_CIRCUIT: &str = "from starkware.cairo.lang.builtins.modulo.mod_builtin_runner import ModBuiltinRunner\nassert builtin_runners[\"add_mod_builtin\"].instance_def.batch_size == 1\nassert builtin_runners[\"mul_mod_builtin\"].instance_def.batch_size == 1\n\nModBuiltinRunner.fill_memory(\n memory=memory,\n add_mod=(ids.add_mod_ptr.address_, builtin_runners[\"add_mod_builtin\"], ids.add_mod_n),\n mul_mod=(ids.mul_mod_ptr.address_, builtin_runners[\"mul_mod_builtin\"], ids.mul_mod_n),\n)"; - -pub const RUN_P_CIRCUIT_WITH_LARGE_BATCH_SIZE: &str = "from starkware.cairo.lang.builtins.modulo.mod_builtin_runner import ModBuiltinRunner\nassert builtin_runners[\"add_mod_builtin\"].instance_def.batch_size == ids.BATCH_SIZE\nassert builtin_runners[\"mul_mod_builtin\"].instance_def.batch_size == ids.BATCH_SIZE\n\nModBuiltinRunner.fill_memory(\n memory=memory,\n add_mod=(ids.add_mod_ptr.address_, builtin_runners[\"add_mod_builtin\"], ids.add_mod_n),\n mul_mod=(ids.mul_mod_ptr.address_, builtin_runners[\"mul_mod_builtin\"], ids.mul_mod_n),\n)"; - -pub const NONDET_ELEMENTS_OVER_TEN: &str = - "memory[ap] = to_felt_or_relocatable(ids.elements_end - ids.elements >= 10)"; -pub const NONDET_ELEMENTS_OVER_TWO: &str = - "memory[ap] = to_felt_or_relocatable(ids.elements_end - ids.elements >= 2)"; - -pub const EXCESS_BALANCE: &str = r#"from excess_balance import excess_balance_func +)"#}, "test_utils"), +(RUN_P_CIRCUIT, "from starkware.cairo.lang.builtins.modulo.mod_builtin_runner import ModBuiltinRunner\nassert builtin_runners[\"add_mod_builtin\"].instance_def.batch_size == 1\nassert builtin_runners[\"mul_mod_builtin\"].instance_def.batch_size == 1\n\nModBuiltinRunner.fill_memory(\n memory=memory,\n add_mod=(ids.add_mod_ptr.address_, builtin_runners[\"add_mod_builtin\"], ids.add_mod_n),\n mul_mod=(ids.mul_mod_ptr.address_, builtin_runners[\"mul_mod_builtin\"], ids.mul_mod_n),\n)"), +(RUN_P_CIRCUIT_WITH_LARGE_BATCH_SIZE, "from starkware.cairo.lang.builtins.modulo.mod_builtin_runner import ModBuiltinRunner\nassert builtin_runners[\"add_mod_builtin\"].instance_def.batch_size == ids.BATCH_SIZE\nassert builtin_runners[\"mul_mod_builtin\"].instance_def.batch_size == ids.BATCH_SIZE\n\nModBuiltinRunner.fill_memory(\n memory=memory,\n add_mod=(ids.add_mod_ptr.address_, builtin_runners[\"add_mod_builtin\"], ids.add_mod_n),\n mul_mod=(ids.mul_mod_ptr.address_, builtin_runners[\"mul_mod_builtin\"], ids.mul_mod_n),\n)"), +(NONDET_ELEMENTS_OVER_TEN, indoc! {r#"memory[ap] = to_felt_or_relocatable(ids.elements_end - ids.elements >= 10)"#}), +(NONDET_ELEMENTS_OVER_TWO, indoc! {r#"memory[ap] = to_felt_or_relocatable(ids.elements_end - ids.elements >= 2)"#}), +(EXCESS_BALANCE, indoc! {r#"from excess_balance import excess_balance_func res = excess_balance_func(ids, memory, __dict_manager) ids.check_account_value = res["account_value"] ids.check_excess_balance = res["excess_balance"] ids.check_margin_requirement_d = res["margin_requirement"] -ids.check_unrealized_pnl_d = res["unrealized_pnl"]"#; +ids.check_unrealized_pnl_d = res["unrealized_pnl"]"#}) +} diff --git a/vm/src/hint_processor/builtin_hint_processor/hint_utils.rs b/vm/src/hint_processor/builtin_hint_processor/hint_utils.rs index e104cb7e86..1da3905e02 100644 --- a/vm/src/hint_processor/builtin_hint_processor/hint_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/hint_utils.rs @@ -15,6 +15,61 @@ use crate::types::relocatable::Relocatable; use crate::vm::errors::hint_errors::HintError; use crate::vm::vm_core::VirtualMachine; +/// Generates a const string for each hint, and a lazy_static HashMap that maps the const name to +/// the hint string. +/// Allows gating specific hints behind feature gates. +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] extern crate cairo_vm; +/// # use cairo_vm::stdlib::collections::HashMap; +/// cairo_vm::define_hint_string_map!( +/// FOO_HINTS, +/// (FOO_HINT_ADD_X_Y, "x + y"), +/// (FOO_HINT_PRINT_X, "print(x)", "test_utils") +/// ); +/// ``` +/// +/// This will generate the following code: +/// +/// ``` +/// # use cairo_vm::stdlib::collections::HashMap; +/// pub const FOO_HINT_ADD_X_Y: &str = "x + y"; +/// #[cfg(feature = "test_utils")] +/// pub const FOO_HINT_PRINT_X: &str = "print(x)"; +/// +/// lazy_static::lazy_static! { +/// pub static ref FOO_HINTS: HashMap<&'static str, &'static str> = { +/// let mut map = HashMap::new(); +/// map.insert("FOO_HINT_ADD_X_Y", FOO_HINT_ADD_X_Y); +/// #[cfg(feature = "test_utils")] +/// map.insert("FOO_HINT_PRINT_X", FOO_HINT_PRINT_X); +/// map +/// }; +/// } +/// ``` +#[macro_export] +macro_rules! define_hint_string_map { + ($hint_set_name:ident, $(($hint_name:ident, $hint_str:expr $(, $feature_gate:expr)?)),+) => { + $( + $(#[cfg(feature = $feature_gate)])? + pub const $hint_name: &str = $hint_str; + )+ + + lazy_static::lazy_static! { + pub static ref $hint_set_name: HashMap<&'static str, &'static str> = { + let mut map = HashMap::new(); + $( + $(#[cfg(feature = $feature_gate)])? + map.insert(stringify!($hint_name), $hint_name); + )+ + map + }; + } + } +} + //Inserts value into the address of the given ids variable pub fn insert_value_from_var_name( var_name: &str, diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/cairo0_hints.rs b/vm/src/hint_processor/builtin_hint_processor/secp/cairo0_hints.rs index 5e894588da..f3ffeac78f 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/cairo0_hints.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/cairo0_hints.rs @@ -5,6 +5,7 @@ use crate::stdlib::{ prelude::*, }; +use crate::define_hint_string_map; use crate::hint_processor::builtin_hint_processor::hint_utils::{ get_constant_from_var_name, get_integer_from_var_name, insert_value_from_var_name, }; @@ -17,6 +18,7 @@ use crate::types::exec_scope::ExecutionScopes; use crate::vm::errors::hint_errors::HintError; use crate::vm::vm_core::VirtualMachine; use crate::Felt252; +use indoc::indoc; use num_bigint::{BigInt, BigUint}; use num_integer::Integer; use num_traits::One; @@ -26,9 +28,70 @@ use super::bigint_utils::{BigInt3, Uint384}; use super::ec_utils::EcPoint; use super::secp_utils::{SECP256R1_ALPHA, SECP256R1_B, SECP256R1_P}; -pub const SECP_REDUCE: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +define_hint_string_map! { + CAIRO0_HINT_CODES, +(SECP_REDUCE, indoc! {r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P from starkware.cairo.common.cairo_secp.secp_utils import pack -value = pack(ids.x, PRIME) % SECP256R1_P"#; +value = pack(ids.x, PRIME) % SECP256R1_P"#}), +(SECP_REDUCE_X, indoc! {r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack + +x = pack(ids.x, PRIME) % SECP256R1_P"#}), +(COMPUTE_Q_MOD_PRIME, indoc! {r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack + +q, r = divmod(pack(ids.val, PRIME), SECP256R1_P) +assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." +ids.q = q % PRIME"#}), +(COMPUTE_IDS_HIGH_LOW, indoc! {r#"from starkware.cairo.common.math_utils import as_int + +# Correctness check. +value = as_int(ids.value, PRIME) % PRIME +assert value < ids.UPPER_BOUND, f'{value} is outside of the range [0, 2**165).' + +# Calculation for the assertion. +ids.high, ids.low = divmod(ids.value, ids.SHIFT)"#}), +(SECP_R1_GET_POINT_FROM_X, indoc! {r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP256R1, pack +from starkware.python.math_utils import y_squared_from_x + +y_square_int = y_squared_from_x( + x=pack(ids.x, SECP256R1.prime), + alpha=SECP256R1.alpha, + beta=SECP256R1.beta, + field_prime=SECP256R1.prime, +) + +# Note that (y_square_int ** ((SECP256R1.prime + 1) / 4)) ** 2 = +# = y_square_int ** ((SECP256R1.prime + 1) / 2) = +# = y_square_int ** ((SECP256R1.prime - 1) / 2 + 1) = +# = y_square_int * y_square_int ** ((SECP256R1.prime - 1) / 2) = y_square_int * {+/-}1. +y = pow(y_square_int, (SECP256R1.prime + 1) // 4, SECP256R1.prime) + +# We need to decide whether to take y or prime - y. +if ids.v % 2 == y % 2: + value = y +else: + value = (-y) % SECP256R1.prime"#}), +(IS_ON_CURVE_2, indoc! {r#"ids.is_on_curve = (y * y) % SECP256R1.prime == y_square_int"#}), +(SECP_DOUBLE_ASSIGN_NEW_X, indoc! {r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack + +slope = pack(ids.slope, SECP256R1_P) +x = pack(ids.point.x, SECP256R1_P) +y = pack(ids.point.y, SECP256R1_P) + +value = new_x = (pow(slope, 2, SECP256R1_P) - 2 * x) % SECP256R1_P"#}), +(GENERATE_NIBBLES, indoc! {r#"num = (ids.scalar.high << 128) + ids.scalar.low +nibbles = [(num >> i) & 0xf for i in range(0, 256, 4)] +ids.first_nibble = nibbles.pop() +ids.last_nibble = nibbles[0]"#}), +(FAST_SECP_ADD_ASSIGN_NEW_Y, indoc! {r#"value = new_y = (slope * (x - new_x) - y) % SECP256R1_P"#}), +(WRITE_NIBBLES_TO_MEM, indoc! {r#"memory[fp + 0] = to_felt_or_relocatable(nibbles.pop())"#}), +(COMPUTE_VALUE_DIV_MOD, indoc! {r#"from starkware.python.math_utils import div_mod + +value = div_mod(1, x, SECP256R1_P)"#}) +} + pub fn reduce_value( vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, @@ -41,10 +104,6 @@ pub fn reduce_value( Ok(()) } -pub const SECP_REDUCE_X: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P -from starkware.cairo.common.cairo_secp.secp_utils import pack - -x = pack(ids.x, PRIME) % SECP256R1_P"#; pub fn reduce_x( vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, @@ -57,12 +116,6 @@ pub fn reduce_x( Ok(()) } -pub const COMPUTE_Q_MOD_PRIME: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P -from starkware.cairo.common.cairo_secp.secp_utils import pack - -q, r = divmod(pack(ids.val, PRIME), SECP256R1_P) -assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." -ids.q = q % PRIME"#; pub fn compute_q_mod_prime( vm: &mut VirtualMachine, _exec_scopes: &mut ExecutionScopes, @@ -79,14 +132,6 @@ pub fn compute_q_mod_prime( Ok(()) } -pub const COMPUTE_IDS_HIGH_LOW: &str = r#"from starkware.cairo.common.math_utils import as_int - -# Correctness check. -value = as_int(ids.value, PRIME) % PRIME -assert value < ids.UPPER_BOUND, f'{value} is outside of the range [0, 2**165).' - -# Calculation for the assertion. -ids.high, ids.low = divmod(ids.value, ids.SHIFT)"#; pub fn compute_ids_high_low( vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, @@ -121,28 +166,6 @@ pub fn compute_ids_high_low( Ok(()) } -pub const SECP_R1_GET_POINT_FROM_X: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP256R1, pack -from starkware.python.math_utils import y_squared_from_x - -y_square_int = y_squared_from_x( - x=pack(ids.x, SECP256R1.prime), - alpha=SECP256R1.alpha, - beta=SECP256R1.beta, - field_prime=SECP256R1.prime, -) - -# Note that (y_square_int ** ((SECP256R1.prime + 1) / 4)) ** 2 = -# = y_square_int ** ((SECP256R1.prime + 1) / 2) = -# = y_square_int ** ((SECP256R1.prime - 1) / 2 + 1) = -# = y_square_int * y_square_int ** ((SECP256R1.prime - 1) / 2) = y_square_int * {+/-}1. -y = pow(y_square_int, (SECP256R1.prime + 1) // 4, SECP256R1.prime) - -# We need to decide whether to take y or prime - y. -if ids.v % 2 == y % 2: - value = y -else: - value = (-y) % SECP256R1.prime"#; - pub fn r1_get_point_from_x( vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, @@ -204,8 +227,6 @@ pub fn r1_get_point_from_x( Ok(()) } -pub const IS_ON_CURVE_2: &str = r#"ids.is_on_curve = (y * y) % SECP256R1.prime == y_square_int"#; - pub fn is_on_curve_2( vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, @@ -228,15 +249,6 @@ pub fn is_on_curve_2( Ok(()) } -pub const SECP_DOUBLE_ASSIGN_NEW_X: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P -from starkware.cairo.common.cairo_secp.secp_utils import pack - -slope = pack(ids.slope, SECP256R1_P) -x = pack(ids.point.x, SECP256R1_P) -y = pack(ids.point.y, SECP256R1_P) - -value = new_x = (pow(slope, 2, SECP256R1_P) - 2 * x) % SECP256R1_P"#; - pub fn secp_double_assign_new_x( vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, @@ -266,10 +278,6 @@ pub fn secp_double_assign_new_x( Ok(()) } -pub const GENERATE_NIBBLES: &str = r#"num = (ids.scalar.high << 128) + ids.scalar.low -nibbles = [(num >> i) & 0xf for i in range(0, 256, 4)] -ids.first_nibble = nibbles.pop() -ids.last_nibble = nibbles[0]"#; pub fn generate_nibbles( vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, @@ -298,8 +306,6 @@ pub fn generate_nibbles( Ok(()) } -pub const FAST_SECP_ADD_ASSIGN_NEW_Y: &str = - r#"value = new_y = (slope * (x - new_x) - y) % SECP256R1_P"#; pub fn fast_secp_add_assign_new_y( _vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, @@ -322,8 +328,6 @@ pub fn fast_secp_add_assign_new_y( Ok(()) } -pub const WRITE_NIBBLES_TO_MEM: &str = r#"memory[fp + 0] = to_felt_or_relocatable(nibbles.pop())"#; - pub fn write_nibbles_to_mem( vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, @@ -338,9 +342,6 @@ pub fn write_nibbles_to_mem( Ok(()) } -pub const COMPUTE_VALUE_DIV_MOD: &str = r#"from starkware.python.math_utils import div_mod - -value = div_mod(1, x, SECP256R1_P)"#; pub fn compute_value_div_mod( _vm: &mut VirtualMachine, exec_scopes: &mut ExecutionScopes, diff --git a/vm/src/tests/cairo_run_test.rs b/vm/src/tests/cairo_run_test.rs index c2dea2f4b2..cbce327c08 100644 --- a/vm/src/tests/cairo_run_test.rs +++ b/vm/src/tests/cairo_run_test.rs @@ -568,6 +568,14 @@ fn blake2s_integration_tests() { run_program_simple(program_data.as_slice()); } +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn blake2s_opcode_test() { + let program_data = + include_bytes!("../../../cairo_programs/stwo_exclusive_programs/blake2s_opcode_test.json"); + run_program_simple(program_data.as_slice()); +} + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn relocate_segments() { diff --git a/vm/src/types/instruction.rs b/vm/src/types/instruction.rs index 133c691302..45cfab1f6f 100644 --- a/vm/src/types/instruction.rs +++ b/vm/src/types/instruction.rs @@ -27,6 +27,7 @@ pub struct Instruction { pub ap_update: ApUpdate, pub fp_update: FpUpdate, pub opcode: Opcode, + pub opcode_extension: OpcodeExtension, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -76,6 +77,12 @@ pub enum Opcode { Ret, } +#[derive(Clone, Debug, Copy, PartialEq, Eq)] +pub enum OpcodeExtension { + Stone, + Blake, +} + impl Instruction { pub fn size(&self) -> usize { match self.op1_addr { @@ -87,11 +94,11 @@ impl Instruction { // Returns True if the given instruction looks like a call instruction pub(crate) fn is_call_instruction(encoded_instruction: &Felt252) -> bool { - let encoded_i64_instruction = match encoded_instruction.to_u64() { + let encoded_u128_instruction = match encoded_instruction.to_u128() { Some(num) => num, None => return false, }; - let instruction = match decode_instruction(encoded_i64_instruction) { + let instruction = match decode_instruction(encoded_u128_instruction) { Ok(inst) => inst, Err(_) => return false, }; @@ -134,7 +141,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn instruction_size() { let encoded_instruction = Felt252::from(1226245742482522112_i64); - let instruction = decode_instruction(encoded_instruction.to_u64().unwrap()).unwrap(); + let instruction = decode_instruction(encoded_instruction.to_u128().unwrap()).unwrap(); assert_eq!(instruction.size(), 2); } } diff --git a/vm/src/vm/context/run_context.rs b/vm/src/vm/context/run_context.rs index d5de5d41f5..e2fdb9e633 100644 --- a/vm/src/vm/context/run_context.rs +++ b/vm/src/vm/context/run_context.rs @@ -106,7 +106,7 @@ mod tests { use super::*; use crate::relocatable; use crate::stdlib::string::ToString; - use crate::types::instruction::{ApUpdate, FpUpdate, Opcode, PcUpdate, Res}; + use crate::types::instruction::{ApUpdate, FpUpdate, Opcode, OpcodeExtension, PcUpdate, Res}; use crate::utils::test_utils::mayberelocatable; use crate::vm::errors::memory_errors::MemoryError; use crate::Felt252; @@ -130,6 +130,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -158,6 +159,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -187,6 +189,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -215,6 +218,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -243,6 +247,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -271,6 +276,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -299,6 +305,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -327,6 +334,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -358,6 +366,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -388,6 +397,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { @@ -420,6 +430,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let run_context = RunContext { diff --git a/vm/src/vm/decoding/decoder.rs b/vm/src/vm/decoding/decoder.rs index 8a018980f4..e395c08795 100644 --- a/vm/src/vm/decoding/decoder.rs +++ b/vm/src/vm/decoding/decoder.rs @@ -1,41 +1,40 @@ use crate::{ types::instruction::{ - ApUpdate, FpUpdate, Instruction, Op1Addr, Opcode, PcUpdate, Register, Res, + ApUpdate, FpUpdate, Instruction, Op1Addr, Opcode, OpcodeExtension, PcUpdate, Register, Res, }, vm::errors::vm_errors::VirtualMachineError, }; -// 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg -// 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 - -/// Decodes an instruction. The encoding is little endian, so flags go from bit 63 to 48. -pub fn decode_instruction(encoded_instr: u64) -> Result { - const HIGH_BIT: u64 = 1u64 << 63; - const DST_REG_MASK: u64 = 0x0001; - const DST_REG_OFF: u64 = 0; - const OP0_REG_MASK: u64 = 0x0002; - const OP0_REG_OFF: u64 = 1; - const OP1_SRC_MASK: u64 = 0x001C; - const OP1_SRC_OFF: u64 = 2; - const RES_LOGIC_MASK: u64 = 0x0060; - const RES_LOGIC_OFF: u64 = 5; - const PC_UPDATE_MASK: u64 = 0x0380; - const PC_UPDATE_OFF: u64 = 7; - const AP_UPDATE_MASK: u64 = 0x0C00; - const AP_UPDATE_OFF: u64 = 10; - const OPCODE_MASK: u64 = 0x7000; - const OPCODE_OFF: u64 = 12; +// opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg +// 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + +/// Decodes an instruction. The encoding is little endian, so flags go from bit 127 to 48. +/// The bits 63 and beyond are reserved for the opcode extension. +/// opcode_extension_num=0 means the instruction is a Stone instruction. +/// opcode_extension_num>0 is for new Stwo opcodes. +pub fn decode_instruction(encoded_instr: u128) -> Result { + const DST_REG_MASK: u128 = 0x0001; + const DST_REG_OFF: u128 = 0; + const OP0_REG_MASK: u128 = 0x0002; + const OP0_REG_OFF: u128 = 1; + const OP1_SRC_MASK: u128 = 0x001C; + const OP1_SRC_OFF: u128 = 2; + const RES_LOGIC_MASK: u128 = 0x0060; + const RES_LOGIC_OFF: u128 = 5; + const PC_UPDATE_MASK: u128 = 0x0380; + const PC_UPDATE_OFF: u128 = 7; + const AP_UPDATE_MASK: u128 = 0x0C00; + const AP_UPDATE_OFF: u128 = 10; + const OPCODE_MASK: u128 = 0x7000; + const OPCODE_OFF: u128 = 12; + const OPCODE_EXTENSION_OFF: u128 = 63; // Flags start on the 48th bit. - const FLAGS_OFFSET: u64 = 48; - const OFF0_OFF: u64 = 0; - const OFF1_OFF: u64 = 16; - const OFF2_OFF: u64 = 32; - const OFFX_MASK: u64 = 0xFFFF; - - if encoded_instr & HIGH_BIT != 0 { - return Err(VirtualMachineError::InstructionNonZeroHighBit); - } + const FLAGS_OFFSET: u128 = 48; + const OFF0_OFF: u128 = 0; + const OFF1_OFF: u128 = 16; + const OFF2_OFF: u128 = 32; + const OFFX_MASK: u128 = 0xFFFF; // Grab offsets and convert them from little endian format. let off0 = decode_offset(encoded_instr >> OFF0_OFF & OFFX_MASK); @@ -53,6 +52,9 @@ pub fn decode_instruction(encoded_instr: u64) -> Result> AP_UPDATE_OFF; let opcode_num = (flags & OPCODE_MASK) >> OPCODE_OFF; + // Grab opcode_extension + let opcode_extension_num = encoded_instr >> OPCODE_EXTENSION_OFF; + // Match each flag to its corresponding enum value let dst_register = if dst_reg_num == 1 { Register::FP @@ -98,6 +100,26 @@ pub fn decode_instruction(encoded_instr: u64) -> Result return Err(VirtualMachineError::InvalidOpcode(opcode_num)), }; + let opcode_extension = match opcode_extension_num { + 0 => OpcodeExtension::Stone, + 1 => { + if opcode != Opcode::NOp + || (op1_addr != Op1Addr::FP && op1_addr != Op1Addr::AP) + || res != Res::Op1 + || pc_update != PcUpdate::Regular + || (ap_update_num != 0 && ap_update_num != 2) + { + return Err(VirtualMachineError::InvalidBlake2sFlags(flags & 0x7FFF)); + }; + OpcodeExtension::Blake + } + _ => { + return Err(VirtualMachineError::InvalidOpcodeExtension( + opcode_extension_num, + )) + } + }; + let ap_update = match (ap_update_num, opcode == Opcode::Call) { (0, true) => ApUpdate::Add2, (0, false) => ApUpdate::Regular, @@ -145,11 +167,12 @@ pub fn decode_instruction(encoded_instr: u64) -> Result isize { - let vectorized_offset: [u8; 8] = offset.to_le_bytes(); +fn decode_offset(offset: u128) -> isize { + let vectorized_offset: [u8; 16] = offset.to_le_bytes(); let offset_16b_encoded = u16::from_le_bytes([vectorized_offset[0], vectorized_offset[1]]); let complement_const = 0x8000u16; let (offset_16b, _) = offset_16b_encoded.overflowing_sub(complement_const); @@ -167,11 +190,11 @@ mod decoder_test { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn non_zero_high_bit() { - let error = decode_instruction(0x94A7800080008000); + fn non_zero_high_bits() { + let error = decode_instruction(0x214a7800080008000); assert_eq!( error.unwrap_err().to_string(), - "Instruction MSB should be 0", + "Invalid opcode extension value: 4", ) } @@ -221,10 +244,10 @@ mod decoder_test { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_flags_nop_add_jmp_add_imm_fp_fp() { - // 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg - // 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 - // | NOp| ADD| JUMP| ADD| IMM| FP| FP - // 0 0 0 0 0 1 0 0 1 0 1 0 0 1 1 1 + // opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Stone| NOp| ADD| JUMP| ADD| IMM| FP| FP + // 0 0 0 0 0 1 0 0 1 0 1 0 0 1 1 1 // 0000 0100 1010 0111 = 0x04A7; offx = 0 let inst = decode_instruction(0x04A7800080008000).unwrap(); assert_matches!(inst.dst_register, Register::FP); @@ -235,15 +258,16 @@ mod decoder_test { assert_matches!(inst.ap_update, ApUpdate::Add); assert_matches!(inst.opcode, Opcode::NOp); assert_matches!(inst.fp_update, FpUpdate::Regular); + assert_matches!(inst.opcode_extension, OpcodeExtension::Stone); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_flags_nop_add1_jmp_rel_mul_fp_ap_ap() { - // 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg - // 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 - // | NOp| ADD1| JUMP_REL| MUL| FP| AP| AP - // 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 0 + // opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Stone| NOp| ADD1| JUMP_REL| MUL| FP| AP| AP + // 0 0 0 0 1 0 0 1 0 1 0 0 1 0 0 0 // 0000 1001 0100 1000 = 0x0948; offx = 0 let inst = decode_instruction(0x0948800080008000).unwrap(); assert_matches!(inst.dst_register, Register::AP); @@ -254,15 +278,16 @@ mod decoder_test { assert_matches!(inst.ap_update, ApUpdate::Add1); assert_matches!(inst.opcode, Opcode::NOp); assert_matches!(inst.fp_update, FpUpdate::Regular); + assert_matches!(inst.opcode_extension, OpcodeExtension::Stone); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_flags_assrt_add_regular_mul_ap_ap_ap() { - // 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg - // 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 - // |ASSRT_EQ| ADD| REGULAR| MUL| AP| AP| AP - // 0 1 0 0 1 0 0 0 0 1 0 1 0 0 0 0 + // opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Stone| ASSRT_EQ| ADD| REGULAR| MUL| AP| AP| AP + // 0 1 0 0 1 0 0 0 0 1 0 1 0 0 0 0 // 0100 1000 0101 0000 = 0x4850; offx = 0 let inst = decode_instruction(0x4850800080008000).unwrap(); assert_matches!(inst.dst_register, Register::AP); @@ -273,15 +298,16 @@ mod decoder_test { assert_matches!(inst.ap_update, ApUpdate::Add1); assert_matches!(inst.opcode, Opcode::AssertEq); assert_matches!(inst.fp_update, FpUpdate::Regular); + assert_matches!(inst.opcode_extension, OpcodeExtension::Stone); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_flags_assrt_add2_jnz_uncon_op0_ap_ap() { - // 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg - // 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 - // |ASSRT_EQ| ADD2| JNZ|UNCONSTRD| OP0| AP| AP - // 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 + // opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Stone| ASSRT_EQ| ADD2| JNZ|UNCONSTRD| OP0| AP| AP + // 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 // 0100 0010 0000 0000 = 0x4200; offx = 0 let inst = decode_instruction(0x4200800080008000).unwrap(); assert_matches!(inst.dst_register, Register::AP); @@ -292,15 +318,16 @@ mod decoder_test { assert_matches!(inst.ap_update, ApUpdate::Regular); assert_matches!(inst.opcode, Opcode::AssertEq); assert_matches!(inst.fp_update, FpUpdate::Regular); + assert_matches!(inst.opcode_extension, OpcodeExtension::Stone); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_flags_nop_regu_regu_op1_op0_ap_ap() { - // 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg - // 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 - // | NOP| REGULAR| REGULAR| OP1| OP0| AP| AP - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Stone| NOP| REGULAR| REGULAR| OP1| OP0| AP| AP + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 // 0000 0000 0000 0000 = 0x0000; offx = 0 let inst = decode_instruction(0x0000800080008000).unwrap(); assert_matches!(inst.dst_register, Register::AP); @@ -311,15 +338,16 @@ mod decoder_test { assert_matches!(inst.ap_update, ApUpdate::Regular); assert_matches!(inst.opcode, Opcode::NOp); assert_matches!(inst.fp_update, FpUpdate::Regular); + assert_matches!(inst.opcode_extension, OpcodeExtension::Stone); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_offset_negative() { - // 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg - // 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 - // | NOP| REGULAR| REGULAR| OP1| OP0| AP| AP - // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Stone| NOP| REGULAR| REGULAR| OP1| OP0| AP| AP + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 // 0000 0000 0000 0000 = 0x0000; offx = 0 let inst = decode_instruction(0x0000800180007FFF).unwrap(); assert_eq!(inst.off0, -1); @@ -330,10 +358,10 @@ mod decoder_test { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_ret_cairo_standard() { - // 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg - // 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 - // | RET| REGULAR| JUMP| Op1| FP| FP| FP - // 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1 + // opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Stone| RET| REGULAR| JUMP| Op1| FP| FP| FP + // 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1 // 0010 0000 1000 1011 = 0x208b; off0 = -2, off1 = -1 let inst = decode_instruction(0x208b7fff7fff7ffe).unwrap(); assert_matches!(inst.opcode, Opcode::Ret); @@ -346,16 +374,17 @@ mod decoder_test { assert_matches!(inst.pc_update, PcUpdate::Jump); assert_matches!(inst.ap_update, ApUpdate::Regular); assert_matches!(inst.fp_update, FpUpdate::Dst); + assert_matches!(inst.opcode_extension, OpcodeExtension::Stone); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_call_cairo_standard() { - // 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg - // 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 - // | CALL| Add2| JumpRel| Op1| FP| FP| FP - // 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 - // 0001 0001 0000 0100 = 0x208b; off0 = 0, off1 = 1 + // opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Stone| CALL| Regular| JumpRel| Op1| FP| FP| FP + // 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 + // 0001 0001 0000 0100 = 0x1104; off0 = 0, off1 = 1 let inst = decode_instruction(0x1104800180018000).unwrap(); assert_matches!(inst.opcode, Opcode::Call); assert_matches!(inst.off0, 0); @@ -367,15 +396,16 @@ mod decoder_test { assert_matches!(inst.pc_update, PcUpdate::JumpRel); assert_matches!(inst.ap_update, ApUpdate::Add2); assert_matches!(inst.fp_update, FpUpdate::APPlus2); + assert_matches!(inst.opcode_extension, OpcodeExtension::Stone); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_ret_opcode_error() { - // 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg - // 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 - // | RET| REGULAR| JUMP| Op1| FP| FP| FP - // 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1 + // opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Stone| RET| REGULAR| JUMP| Op1| FP| FP| FP + // 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 1 // 0010 0000 1000 1011 = 0x208b; off0 = -1, off1 = -1 let error = decode_instruction(0x208b7fff7fff7fff); assert_matches!(error, Err(VirtualMachineError::InvalidOpcode(2))); @@ -384,12 +414,70 @@ mod decoder_test { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_call_opcode_error() { - // 0| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg - // 15|14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 - // | CALL| Add2| JumpRel| Op1| FP| FP| FP - // 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 - // 0001 0001 0000 0100 = 0x208b; off0 = 1, off1 = 1 + // opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Stone| CALL| REGULAR| JumpRel| Op1| IMM| AP| AP + // 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 + // 0001 0001 0000 0100 = 0x1104; off0 = 1, off1 = 1 let error = decode_instruction(0x1104800180018001); assert_matches!(error, Err(VirtualMachineError::InvalidOpcode(1))); } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn decode_opcode_extension_clash() { + // opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Blake| CALL| REGULAR| REGULAR| Op1| FP| AP| AP + // 1 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 + // 1001 0000 0000 1000 = 0x9008; off0 = 1, off1 = 1 + let error = decode_instruction(0x9008800180018001); + assert_matches!(error, Err(VirtualMachineError::InvalidBlake2sFlags(4104))); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn decode_blake_imm() { + // opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Blake| NOP| REGULAR| REGULAR| Op1| IMM| AP| AP + // 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 + // 1000 0000 0000 0100 = 0x8004; off0 = 1, off1 = 1 + let error = decode_instruction(0x8004800180018001); + assert_matches!(error, Err(VirtualMachineError::InvalidBlake2sFlags(4))); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn decode_blake() { + // opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // Blake| NOP| ADD1| REGULAR| Op1| AP| FP| FP + // 1 0 0 0 1 0 0 0 0 0 0 1 0 0 1 1 + // 1000 1000 0001 0011 = 0x8813; off0 = 1, off1 = 1 + let inst = decode_instruction(0x8813800180018001).unwrap(); + assert_matches!(inst.opcode, Opcode::NOp); + assert_matches!(inst.off0, 1); + assert_matches!(inst.off1, 1); + assert_matches!(inst.dst_register, Register::FP); + assert_matches!(inst.op0_register, Register::FP); + assert_matches!(inst.op1_addr, Op1Addr::AP); + assert_matches!(inst.res, Res::Op1); + assert_matches!(inst.pc_update, PcUpdate::Regular); + assert_matches!(inst.ap_update, ApUpdate::Add1); + assert_matches!(inst.fp_update, FpUpdate::Regular); + assert_matches!(inst.opcode_extension, OpcodeExtension::Blake); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn decode_invalid_opcode_extension_error() { + // opcode_extension| opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg + // 79 ... 17 16 15| 14 13 12| 11 10| 9 8 7| 6 5|4 3 2| 1| 0 + // ???| CALL| Add2| JumpRel| Op1| IMM| FP| FP + // 0 1 1 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 + // 0001 1001 0001 0000 0100 = 0x39104; off0 = 0, off1 = 1 + let error = decode_instruction(0x19104800180018000); + assert_matches!(error, Err(VirtualMachineError::InvalidOpcodeExtension(3))); + } } diff --git a/vm/src/vm/errors/vm_errors.rs b/vm/src/vm/errors/vm_errors.rs index c3698eda02..9162d591bd 100644 --- a/vm/src/vm/errors/vm_errors.rs +++ b/vm/src/vm/errors/vm_errors.rs @@ -34,20 +34,18 @@ pub enum VirtualMachineError { MainScopeError(#[from] ExecScopeError), #[error(transparent)] Other(anyhow::Error), - #[error("Instruction MSB should be 0")] - InstructionNonZeroHighBit, #[error("Instruction should be an int")] InvalidInstructionEncoding, #[error("Invalid op1_register value: {0}")] - InvalidOp1Reg(u64), + InvalidOp1Reg(u128), #[error("In immediate mode, off2 should be 1")] ImmShouldBe1, #[error("op0 must be known in double dereference")] UnknownOp0, #[error("Invalid ap_update value: {0}")] - InvalidApUpdate(u64), + InvalidApUpdate(u128), #[error("Invalid pc_update value: {0}")] - InvalidPcUpdate(u64), + InvalidPcUpdate(u128), #[error("Res.UNCONSTRAINED cannot be used with ApUpdate.ADD")] UnconstrainedResAdd, #[error("Res.UNCONSTRAINED cannot be used with PcUpdate.JUMP")] @@ -73,9 +71,11 @@ pub enum VirtualMachineError { #[error("Couldn't get or load dst")] NoDst, #[error("Invalid res value: {0}")] - InvalidRes(u64), + InvalidRes(u128), #[error("Invalid opcode value: {0}")] - InvalidOpcode(u64), + InvalidOpcode(u128), + #[error("Invalid opcode extension value: {0}")] + InvalidOpcodeExtension(u128), #[error("This is not implemented")] NotImplemented, #[error("Inconsistent auto-deduction for {}, expected {}, got {:?}", (*.0).0, (*.0).1, (*.0).2)] @@ -136,6 +136,10 @@ pub enum VirtualMachineError { RelocationNotFound(usize), #[error("{} batch size is not {}", (*.0).0, (*.0).1)] ModBuiltinBatchSize(Box<(BuiltinName, usize)>), + #[error("Blake2s opcode invalid operand: op{0} does not point to {1} u32 numbers.")] + Blake2sInvalidOperand(u8, u8), + #[error("Blake2s opcode invalid flags {0}")] + InvalidBlake2sFlags(u128), } #[cfg(test)] diff --git a/vm/src/vm/vm_core.rs b/vm/src/vm/vm_core.rs index c04fd3721e..d573f65088 100644 --- a/vm/src/vm/vm_core.rs +++ b/vm/src/vm/vm_core.rs @@ -1,10 +1,14 @@ use crate::math_utils::signed_felt; use crate::stdlib::{any::Any, borrow::Cow, collections::HashMap, prelude::*}; use crate::types::builtin_name::BuiltinName; +use crate::types::instruction::OpcodeExtension; #[cfg(feature = "extensive_hints")] use crate::types::program::HintRange; use crate::{ - hint_processor::hint_processor_definition::HintProcessor, + hint_processor::{ + builtin_hint_processor::blake2s_hash::blake2s_compress, + hint_processor_definition::HintProcessor, + }, types::{ errors::math_errors::MathError, exec_scope::ExecutionScopes, @@ -441,18 +445,72 @@ impl VirtualMachine { .memory .mark_as_accessed(operands_addresses.op1_addr); + if instruction.opcode_extension == OpcodeExtension::Blake { + self.handle_blake2s_instruction(&operands_addresses)?; + } + self.update_registers(instruction, operands)?; self.current_step += 1; Ok(()) } + /// Executes a Blake2s instruction. + /// Expects operands to be RelocatableValue and to point to segments of memory. + /// op0 is expected to point to a sequence of 8 u32 values (state). + /// op1 is expected to point to a sequence of 16 u32 values (message). + /// dst is expected hold the u32 value of the counter (t). + /// [ap] is expected to point to a sequence of 8 cells each being either unitialised or + /// containing the Blake2s compression output at that index. + /// Deviation from the aforementioned expectations will result in an error. + /// The instruction will update the memory segment pointed by [ap] with the new state. + /// Note: the byte counter should count the number of message bytes processed so far including + /// the current portion of the message (i.e. it starts at 64, not 0). + fn handle_blake2s_instruction( + &mut self, + operands_addresses: &OperandsAddresses, + ) -> Result<(), VirtualMachineError> { + let counter = self.segments.memory.get_u32(operands_addresses.dst_addr)?; + + let state: [u32; 8] = (self.get_u32_range( + self.segments + .memory + .get_relocatable(operands_addresses.op0_addr)?, + 8, + )?) + .try_into() + .map_err(|_| VirtualMachineError::Blake2sInvalidOperand(0, 8))?; + + let message: [u32; 16] = (self.get_u32_range( + self.segments + .memory + .get_relocatable(operands_addresses.op1_addr)?, + 16, + )?) + .try_into() + .map_err(|_| VirtualMachineError::Blake2sInvalidOperand(1, 16))?; + + let ap = self.run_context.get_ap(); + let output_address = self.segments.memory.get_relocatable(ap)?; + + let new_state = blake2s_compress(&state, &message, counter, 0, 0, 0); + + for (i, &val) in new_state.iter().enumerate() { + self.segments.memory.insert_as_accessed( + (output_address + i)?, + MaybeRelocatable::Int(Felt252::from(val)), + )?; + } + + Ok(()) + } + fn decode_current_instruction(&self) -> Result { let instruction = self .segments .memory .get_integer(self.run_context.pc)? - .to_u64() + .to_u128() .ok_or(VirtualMachineError::InvalidInstructionEncoding)?; decode_instruction(instruction) } @@ -929,6 +987,12 @@ impl VirtualMachine { self.segments.memory.get_integer_range(addr, size) } + /// Gets n u32 values from memory starting from addr (n being size). + /// Returns an error if any of the values inside the range is missing (memory gap) or is not a u32. + pub fn get_u32_range(&self, addr: Relocatable, size: usize) -> Result, MemoryError> { + self.segments.memory.get_u32_range(addr, size) + } + pub fn get_range_check_builtin( &self, ) -> Result<&RangeCheckBuiltinRunner, VirtualMachineError> { @@ -1268,6 +1332,7 @@ mod tests { use super::*; use crate::felt_hex; use crate::stdlib::collections::HashMap; + use crate::types::instruction::OpcodeExtension; use crate::types::layout_name::LayoutName; use crate::types::program::Program; use crate::{ @@ -1306,6 +1371,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::APPlus2, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1339,6 +1405,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Dst, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1372,6 +1439,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1405,6 +1473,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Dst, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1439,6 +1508,7 @@ mod tests { ap_update: ApUpdate::Add, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1475,6 +1545,7 @@ mod tests { ap_update: ApUpdate::Add, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1510,6 +1581,7 @@ mod tests { ap_update: ApUpdate::Add1, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1546,6 +1618,7 @@ mod tests { ap_update: ApUpdate::Add2, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1582,6 +1655,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1618,6 +1692,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1651,6 +1726,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1684,6 +1760,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1717,6 +1794,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1752,6 +1830,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1786,6 +1865,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1818,6 +1898,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1849,6 +1930,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1882,6 +1964,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1915,6 +1998,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -1953,6 +2037,7 @@ mod tests { ap_update: ApUpdate::Add2, fp_update: FpUpdate::Dst, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -2003,6 +2088,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::Call, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2031,6 +2117,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2063,6 +2150,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2090,6 +2178,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2122,6 +2211,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2151,6 +2241,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2180,6 +2271,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::Ret, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2210,6 +2302,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::Call, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2237,6 +2330,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2268,6 +2362,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2294,6 +2389,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2325,6 +2421,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2354,6 +2451,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2382,6 +2480,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2412,6 +2511,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2441,6 +2541,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2470,6 +2571,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2499,6 +2601,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2526,6 +2629,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2553,6 +2657,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2579,6 +2684,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2601,6 +2707,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::Call, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2626,6 +2733,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::Ret, + opcode_extension: OpcodeExtension::Stone, }; let vm = vm!(); @@ -2648,6 +2756,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let mut vm = vm!(); @@ -2708,6 +2817,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let mut vm = vm!(); //Create program and execution segments @@ -2767,6 +2877,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let mut vm = vm!(); @@ -2822,6 +2933,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::Regular, opcode: Opcode::NOp, + opcode_extension: OpcodeExtension::Stone, }; let mut vm = vm!(); @@ -2847,6 +2959,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::APPlus2, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -2877,6 +2990,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::APPlus2, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -2911,6 +3025,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::APPlus2, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -2943,6 +3058,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::APPlus2, opcode: Opcode::Call, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -2976,6 +3092,7 @@ mod tests { ap_update: ApUpdate::Regular, fp_update: FpUpdate::APPlus2, opcode: Opcode::Call, + opcode_extension: OpcodeExtension::Stone, }; let operands = Operands { @@ -3343,6 +3460,7 @@ mod tests { ap_update: ApUpdate::Add1, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let mut builtin = HashBuiltinRunner::new(Some(8), true); builtin.base = 3; @@ -3431,6 +3549,7 @@ mod tests { ap_update: ApUpdate::Add1, fp_update: FpUpdate::Regular, opcode: Opcode::AssertEq, + opcode_extension: OpcodeExtension::Stone, }; let mut builtin = BitwiseBuiltinRunner::new(Some(256), true); @@ -4126,7 +4245,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn decode_current_instruction_invalid_encoding() { let mut vm = vm!(); - vm.segments = segments![((0, 0), ("112233445566778899", 16))]; + vm.segments = segments![((0, 0), ("112233445566778899112233445566778899", 16))]; assert_matches!( vm.decode_current_instruction(), Err(VirtualMachineError::InvalidInstructionEncoding) @@ -4318,6 +4437,216 @@ mod tests { ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_ok() { + let mut vm = vm!(); + vm.segments.memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 4294967295), ((0, 3), 3)]; + let expected_vector = vec![1, 4294967295]; + assert_eq!(vm.get_u32_range((0, 1).into(), 2), Ok(expected_vector)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_relocatable() { + let mut vm = vm!(); + vm.segments.memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), (0, 0)), ((0, 3), 3)]; + assert_matches!(vm.get_u32_range((0, 1).into(), 2), Err(MemoryError::ExpectedInteger(bx)) if *bx == (0, 2).into()); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_over_32_bits() { + let mut vm = vm!(); + vm.segments.memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 4294967296), ((0, 3), 3)]; + assert_matches!(vm.get_u32_range((0, 1).into(), 2), Err(MemoryError::Math(MathError::Felt252ToU32Conversion(bx))) if *bx == Felt252::from(4294967296_u64)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_memory_gap() { + let mut vm = vm!(); + vm.segments.memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 3), 3)]; + assert_matches!(vm.get_u32_range((0, 1).into(), 3), Err(MemoryError::UnknownMemoryCell(bx)) if *bx == (0, 2).into()); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn handle_blake2s_instruction_state_too_short() { + let mut vm = vm!(); + vm.segments.memory = memory![ + ((0, 0), 0), + ((0, 1), 0), + ((0, 2), 0), + ((0, 3), 0), + ((0, 4), 0), + ((0, 5), 0), + ((0, 6), 0), + ((2, 0), (0, 0)) + ]; + let operands_addresses = OperandsAddresses { + dst_addr: (0, 0).into(), + op0_addr: (2, 0).into(), + op1_addr: (2, 0).into(), + }; + vm.run_context = RunContext { + pc: (0, 0).into(), + ap: 0, + fp: 0, + }; + + assert_matches!( + vm.handle_blake2s_instruction(&operands_addresses), + Err(VirtualMachineError::Memory(MemoryError::UnknownMemoryCell(bx))) if *bx == (0, 7).into() + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn handle_blake2s_instruction_message_too_short() { + let mut vm = vm!(); + vm.segments.memory = memory![ + ((0, 0), 0), + ((0, 1), 0), + ((0, 2), 0), + ((0, 3), 0), + ((0, 4), 0), + ((0, 5), 0), + ((0, 6), 0), + ((0, 7), 0), + ((2, 0), (0, 0)) + ]; + let operands_addresses = OperandsAddresses { + dst_addr: (0, 0).into(), + op0_addr: (2, 0).into(), + op1_addr: (2, 0).into(), + }; + vm.run_context = RunContext { + pc: (0, 0).into(), + ap: 0, + fp: 0, + }; + + assert_matches!( + vm.handle_blake2s_instruction(&operands_addresses), + Err(VirtualMachineError::Memory(MemoryError::UnknownMemoryCell(bx))) if *bx == (0, 8).into() + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn handle_blake2s_instruction_ap_points_to_inconsistent_memory() { + let mut vm = vm!(); + vm.segments.memory = memory![ + ((0, 0), 0), + ((0, 1), 0), + ((0, 2), 0), + ((0, 3), 0), + ((0, 4), 0), + ((0, 5), 0), + ((0, 6), 0), + ((0, 7), 0), + ((0, 8), 0), + ((0, 9), 0), + ((0, 10), 0), + ((0, 11), 0), + ((0, 12), 0), + ((0, 13), 0), + ((0, 14), 0), + ((0, 15), 0), + ((1, 0), (0, 0)) + ]; + let operands_addresses = OperandsAddresses { + dst_addr: (0, 0).into(), + op0_addr: (1, 0).into(), + op1_addr: (1, 0).into(), + }; + vm.run_context = RunContext { + pc: (0, 0).into(), + ap: 0, + fp: 0, + }; + + assert_matches!( + vm.handle_blake2s_instruction(&operands_addresses), + Err(VirtualMachineError::Memory(MemoryError::InconsistentMemory(bx))) if *bx == ((0, 0).into(),0.into(),1848029226.into()) + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn handle_blake2s_instruction_ok() { + let mut vm = vm!(); + vm.segments.memory = memory![ + // State + ((0, 0), 0x6B08E647), + ((0, 1), 0xBB67AE85), + ((0, 2), 0x3C6EF372), + ((0, 3), 0xA54FF53A), + ((0, 4), 0x510E527F), + ((0, 5), 0x9B05688C), + ((0, 6), 0x1F83D9AB), + ((0, 7), 0x5BE0CD19), + // Message + ((0, 8), 930933030), + ((0, 9), 1766240503), + ((0, 10), 3660871006), + ((0, 11), 388409270), + ((0, 12), 1948594622), + ((0, 13), 3119396969), + ((0, 14), 3924579183), + ((0, 15), 2089920034), + ((0, 16), 3857888532), + ((0, 17), 929304360), + ((0, 18), 1810891574), + ((0, 19), 860971754), + ((0, 20), 1822893775), + ((0, 21), 2008495810), + ((0, 22), 2958962335), + ((0, 23), 2340515744), + // Counter + ((0, 24), 64), + // AP + ((1, 0), (0, 25)), + ((2, 0), (0, 0)), + ((2, 1), (0, 8)) + ]; + let operands_addresses = OperandsAddresses { + dst_addr: (0, 24).into(), + op0_addr: (2, 0).into(), + op1_addr: (2, 1).into(), + }; + vm.run_context = RunContext { + pc: (0, 0).into(), + ap: 0, + fp: 0, + }; + assert_matches!(vm.handle_blake2s_instruction(&operands_addresses), Ok(())); + + let state: [u32; 8] = vm + .get_u32_range((0, 0).into(), 8) + .unwrap() + .try_into() + .unwrap(); + let message: [u32; 16] = vm + .get_u32_range((0, 8).into(), 16) + .unwrap() + .try_into() + .unwrap(); + let counter = vm.segments.memory.get_u32((0, 24).into()).unwrap(); + + let expected_new_state: [u32; 8] = blake2s_compress(&state, &message, counter, 0, 0, 0) + .try_into() + .unwrap(); + + let new_state: [u32; 8] = vm + .get_u32_range((0, 25).into(), 8) + .unwrap() + .try_into() + .unwrap(); + assert_eq!(new_state, expected_new_state); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_traceback_entries_bad_usort() { diff --git a/vm/src/vm/vm_memory/memory.rs b/vm/src/vm/vm_memory/memory.rs index affd0a06d4..9a20b60d05 100644 --- a/vm/src/vm/vm_memory/memory.rs +++ b/vm/src/vm/vm_memory/memory.rs @@ -414,6 +414,14 @@ impl Memory { } } + /// Gets a u32 value from memory address. + /// Returns an Error if the value at the memory address is missing or not a u32. + pub fn get_u32(&self, key: Relocatable) -> Result { + let felt = self.get_integer(key)?.into_owned(); + felt.to_u32() + .ok_or_else(|| MemoryError::Math(MathError::Felt252ToU32Conversion(Box::new(felt)))) + } + /// Gets the value from memory address as a usize. /// Returns an Error if the value at the memory address is missing not a Felt252, or can't be converted to usize. pub fn get_usize(&self, key: Relocatable) -> Result { @@ -623,6 +631,18 @@ impl Memory { Ok(values) } + /// Gets a range of u32 memory values from addr to addr + size + /// Fails if any of the values inside the range is missing (memory gap) or is not a u32 + pub fn get_u32_range(&self, addr: Relocatable, size: usize) -> Result, MemoryError> { + let mut values = Vec::new(); + + for i in 0..size { + values.push(self.get_u32((addr + i)?)?); + } + + Ok(values) + } + pub fn mark_as_accessed(&mut self, addr: Relocatable) { let (i, j) = from_relocatable_to_indexes(addr); let data = if addr.segment_index < 0 { @@ -1135,6 +1155,23 @@ mod memory_tests { ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_too_big() { + let mut segments = MemorySegmentManager::new(); + segments.add(); + segments + .memory + .insert(Relocatable::from((0, 0)), &Felt252::from(1_u64 << 32)) + .unwrap(); + assert_matches!( + segments.memory.get_u32(Relocatable::from((0, 0))), + Err(MemoryError::Math(MathError::Felt252ToU32Conversion( + bx + ))) if *bx == Felt252::from(1_u64 << 32) + ); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn default_memory() { @@ -1350,6 +1387,35 @@ mod memory_tests { ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_ok() { + let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 4294967295), ((0, 3), 3)]; + let expected_vector = vec![1, 4294967295]; + assert_eq!(memory.get_u32_range((0, 1).into(), 2), Ok(expected_vector)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_relocatable() { + let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), (0, 0)), ((0, 3), 3)]; + assert_matches!(memory.get_u32_range((0, 1).into(), 2), Err(MemoryError::ExpectedInteger(bx)) if *bx == (0, 2).into()); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_over_32_bits() { + let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 4294967296), ((0, 3), 3)]; + assert_matches!(memory.get_u32_range((0, 1).into(), 2), Err(MemoryError::Math(MathError::Felt252ToU32Conversion(bx))) if *bx == Felt252::from(4294967296_u64)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_u32_range_memory_gap() { + let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 3), 3)]; + assert_matches!(memory.get_u32_range((0, 1).into(), 3), Err(MemoryError::UnknownMemoryCell(bx)) if *bx == (0, 2).into()); + } + /// Test that relocate_memory() works when there are no relocation rules. #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]