diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b58b6d84d8..6a76308239 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -323,7 +323,7 @@ jobs: strategy: fail-fast: false matrix: - special_features: ["", "extensive_hints", "mod_builtin", "cairo-0-secp-hints"] + special_features: ["", "extensive_hints", "mod_builtin", "cairo-0-secp-hints", "cairo-0-data-availability-hints"] target: [ test#1, test#2, test#3, test#4, test-no_std#1, test-no_std#2, test-no_std#3, test-no_std#4, test-wasm ] name: Run tests runs-on: ubuntu-22.04 diff --git a/CHANGELOG.md b/CHANGELOG.md index dcc5f7e6ec..fb3f2646d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ #### Upcoming Changes +* feat: implement `kzg` data availability hints [#1887](https://github.com/lambdaclass/cairo-vm/pull/1887) + #### [2.0.0-rc3] - 2024-12-26 * chore: update cairo-lang dependencies to 2.10.0-rc.0 #[1901](https://github.com/lambdaclass/cairo-vm/pull/1901) diff --git a/Makefile b/Makefile index 7468bd0240..9b60105ab3 100644 --- a/Makefile +++ b/Makefile @@ -94,6 +94,10 @@ SECP_CAIRO0_HINTS_DIR=cairo_programs/cairo-0-secp-hints-feature SECP_CAIRO0_HINTS_FILES:=$(wildcard $(SECP_CAIRO0_HINTS_DIR)/*.cairo) COMPILED_SECP_CAIRO0_HINTS:=$(patsubst $(SECP_CAIRO0_HINTS_DIR)/%.cairo, $(SECP_CAIRO0_HINTS_DIR)/%.json, $(SECP_CAIRO0_HINTS_FILES)) +KZG_DA_CAIRO0_HINTS_DIR=cairo_programs/cairo-0-kzg-da-hints +KZG_DA_CAIRO0_HINTS_FILES:=$(wildcard $(KZG_DA_CAIRO0_HINTS_DIR)/*.cairo) +COMPILED_KZG_DA_CAIRO0_HINTS:=$(patsubst $(KZG_DA_CAIRO0_HINTS_DIR)/%.cairo, $(KZG_DA_CAIRO0_HINTS_DIR)/%.json, $(KZG_DA_CAIRO0_HINTS_FILES)) + PRINT_TEST_DIR=cairo_programs/print_feature PRINT_TEST_FILES:=$(wildcard $(PRINT_TEST_DIR)/*.cairo) COMPILED_PRINT_TESTS:=$(patsubst $(PRINT_TEST_DIR)/%.cairo, $(PRINT_TEST_DIR)/%.json, $(PRINT_TEST_FILES)) @@ -243,7 +247,7 @@ run: check: cargo check -cairo_test_programs: $(COMPILED_TESTS) $(COMPILED_BAD_TESTS) $(COMPILED_NORETROCOMPAT_TESTS) $(COMPILED_PRINT_TESTS) $(COMPILED_MOD_BUILTIN_TESTS) $(COMPILED_SECP_CAIRO0_HINTS) +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_bench_programs: $(COMPILED_BENCHES) cairo_1_test_contracts: $(CAIRO_1_COMPILED_CASM_CONTRACTS) @@ -268,7 +272,7 @@ test-wasm: cairo_proof_programs cairo_test_programs # NOTE: release mode is needed to avoid "too many locals" error wasm-pack test --release --node vm --no-default-features test-extensive_hints: cairo_proof_programs cairo_test_programs - $(TEST_COMMAND) --workspace --features "test_utils, cairo-1-hints, cairo-0-secp-hints, extensive_hints" + $(TEST_COMMAND) --workspace --features "test_utils, cairo-1-hints, cairo-0-secp-hints, cairo-0-data-availability-hints, extensive_hints" check-fmt: cargo fmt --all -- --check @@ -349,6 +353,7 @@ clean: rm -f $(BENCH_DIR)/*.json rm -f $(BAD_TEST_DIR)/*.json rm -f $(SECP_CAIRO0_HINTS_DIR)/*.json + rm -f $(KZG_DA_CAIRO0_HINTS_DIR)/*.json rm -f $(PRINT_TEST_DIR)/*.json rm -f $(CAIRO_1_CONTRACTS_TEST_DIR)/*.sierra rm -f $(CAIRO_1_CONTRACTS_TEST_DIR)/*.casm diff --git a/cairo_programs/cairo-0-kzg-da-hints/reduced_mul.cairo b/cairo_programs/cairo-0-kzg-da-hints/reduced_mul.cairo new file mode 100644 index 0000000000..8f54d7d0cb --- /dev/null +++ b/cairo_programs/cairo-0-kzg-da-hints/reduced_mul.cairo @@ -0,0 +1,29 @@ +%builtins range_check + +from starkware.starknet.core.os.data_availability.bls_field import reduced_mul, BigInt3 + +func main{range_check_ptr: felt}() { + let x = BigInt3(0, 0, 0); + let y = BigInt3(1, 1, 1); + + let res = reduced_mul(x, y); + + assert res = BigInt3(0, 0, 0); + + let x = BigInt3(100, 99, 98); + let y = BigInt3(10, 9, 8); + + let res = reduced_mul(x, y); + + assert res = BigInt3( + 49091481911800146991175221, 43711329369885800715738617, 405132241597509509195407 + ); + + let x = BigInt3(47503316700827173496989353, 17218105161352860131668522, 527908748911931938599018); + let y = BigInt3(50964737623371959432443726, 60451660835701602854498663, 5043009036652075489876599); + + let res = reduced_mul(x, y); + assert res = BigInt3(43476011663489831917914902, 15057238271740518603165849, 1923992965848504555868221); + + return (); +} diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 3437a30034..a01a0ce2c0 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -30,6 +30,7 @@ cairo-1-hints = [ tracer = [] mod_builtin = [] cairo-0-secp-hints = [] +cairo-0-data-availability-hints = [] # Note that these features are not retro-compatible with the cairo Python VM. test_utils = ["std", "dep:arbitrary", "starknet-types-core/arbitrary", "starknet-types-core/std"] # This feature will reference every test-oriented feature diff --git a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index 15c8318b15..f5ae7a96c4 100644 --- a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -966,6 +966,14 @@ impl HintProcessorLogic for BuiltinHintProcessor { &hint_data.ap_tracking, constants, ), + #[cfg(feature = "cairo-0-data-availability-hints")] + super::kzg_da::WRITE_DIVMOD_SEGMENT => super::kzg_da::write_div_mod_segment( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), code => Err(HintError::UnknownHint(code.to_string().into_boxed_str())), } diff --git a/vm/src/hint_processor/builtin_hint_processor/kzg_da/mod.rs b/vm/src/hint_processor/builtin_hint_processor/kzg_da/mod.rs new file mode 100644 index 0000000000..8da7274056 --- /dev/null +++ b/vm/src/hint_processor/builtin_hint_processor/kzg_da/mod.rs @@ -0,0 +1,108 @@ +use core::str::FromStr; + +use super::{ + hint_utils::get_relocatable_from_var_name, + secp::{bigint_utils::BigInt3, secp_utils::SECP_P}, +}; +use crate::{ + hint_processor::hint_processor_definition::HintReference, + serde::deserialize_program::ApTracking, + types::relocatable::MaybeRelocatable, + vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, + Felt252, +}; +use crate::{ + stdlib::{collections::HashMap, ops::Deref, prelude::*}, + types::exec_scope::ExecutionScopes, +}; +use lazy_static::lazy_static; +use num_bigint::BigInt; +use num_integer::Integer; +use num_traits::FromPrimitive; +use num_traits::Zero; + +lazy_static! { + static ref BLS_BASE: BigInt = BigInt::from_u64(2).unwrap().pow(86); + static ref BLS_PRIME: BigInt = BigInt::from_str( + "52435875175126190479447740508185965837690552500527637822603658699938581184513" + ) + .unwrap(); +} +pub const WRITE_DIVMOD_SEGMENT: &str = r#"from starkware.starknet.core.os.data_availability.bls_utils import BLS_PRIME, pack, split + +a = pack(ids.a, PRIME) +b = pack(ids.b, PRIME) + +q, r = divmod(a * b, BLS_PRIME) + +# By the assumption: |a|, |b| < 2**104 * ((2**86) ** 2 + 2**86 + 1) < 2**276.001. +# Therefore |q| <= |ab| / BLS_PRIME < 2**299. +# Hence the absolute value of the high limb of split(q) < 2**127. +segments.write_arg(ids.q.address_, split(q)) +segments.write_arg(ids.res.address_, split(r))"#; + +pub fn write_div_mod_segment( + vm: &mut VirtualMachine, + _exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let a = bls_pack( + &BigInt3::from_var_name("a", vm, ids_data, ap_tracking)?, + &SECP_P, + ); + let b = bls_pack( + &BigInt3::from_var_name("b", vm, ids_data, ap_tracking)?, + &SECP_P, + ); + let (q, r) = (a * b).div_mod_floor(&BLS_PRIME); + let q_reloc = get_relocatable_from_var_name("q", vm, ids_data, ap_tracking)?; + let res_reloc = get_relocatable_from_var_name("res", vm, ids_data, ap_tracking)?; + + let q_arg: Vec = bls_split(q) + .into_iter() + .map(|ref n| Felt252::from(n).into()) + .collect::>(); + let res_arg: Vec = bls_split(r) + .into_iter() + .map(|ref n| Felt252::from(n).into()) + .collect::>(); + vm.write_arg(q_reloc, &q_arg).map_err(HintError::Memory)?; + vm.write_arg(res_reloc, &res_arg) + .map_err(HintError::Memory)?; + Ok(()) +} + +fn bls_split(mut num: BigInt) -> Vec { + use num_traits::Signed; + let mut a = Vec::new(); + for _ in 0..2 { + let residue = &num % BLS_BASE.deref(); + num /= BLS_BASE.deref(); + a.push(residue); + } + assert!(num.abs() < BigInt::from_u128(1 << 127).unwrap()); + a.push(num); + a +} + +fn as_int(value: BigInt, prime: &BigInt) -> BigInt { + let half_prime = prime / 2u32; + if value > half_prime { + value - prime + } else { + value + } +} + +fn bls_pack(z: &BigInt3, prime: &BigInt) -> BigInt { + let limbs = &z.limbs; + limbs + .iter() + .enumerate() + .fold(BigInt::zero(), |acc, (i, limb)| { + let limb_as_int = as_int(limb.to_bigint(), prime); + acc + limb_as_int * &BLS_BASE.pow(i as u32) + }) +} diff --git a/vm/src/hint_processor/builtin_hint_processor/mod.rs b/vm/src/hint_processor/builtin_hint_processor/mod.rs index dd647ca944..497c556913 100644 --- a/vm/src/hint_processor/builtin_hint_processor/mod.rs +++ b/vm/src/hint_processor/builtin_hint_processor/mod.rs @@ -38,3 +38,7 @@ pub mod uint384_extension; pub mod uint_utils; pub mod usort; pub mod vrf; + +#[cfg(feature = "cairo-0-data-availability-hints")] +#[cfg_attr(docsrs, doc(cfg(feature = "cairo-0-data-availability-hints")))] +pub mod kzg_da; diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 90545d0f47..22798e3f97 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -9,6 +9,7 @@ //! - implementations of [`arbitrary::Arbitrary`](https://docs.rs/arbitrary/latest/arbitrary/) for some structs. //! - `cairo-1-hints`: Enable hints that were introduced in Cairo 1. Not enabled by default. //! - `cairo-0-secp-hints`: Enable secp hints that were introduced in Cairo 0. Not enabled by default. +//! - `cairo-0-data-availability-hints`: Enable data availability hints that were introduced in Cairo 0. Not enabled by default. #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(warnings)] diff --git a/vm/src/tests/cairo_run_test.rs b/vm/src/tests/cairo_run_test.rs index f33ed945de..d847852929 100644 --- a/vm/src/tests/cairo_run_test.rs +++ b/vm/src/tests/cairo_run_test.rs @@ -1308,3 +1308,12 @@ fn cairo_run_secp_cairo0_ec_mul_by_uint256() { ); run_program_simple(program_data.as_slice()); } + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-data-availability-hints")] +fn cairo_run_data_availability_reduced_mul() { + let program_data = + include_bytes!("../../../cairo_programs/cairo-0-kzg-da-hints/reduced_mul.json"); + run_program_simple(program_data.as_slice()); +}