-
Notifications
You must be signed in to change notification settings - Fork 61
blockifier: move execution utils logic to casm_estimation #8693
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: avivg/blockifier/encode_and_blake_hash_resources_in_trait
Are you sure you want to change the base?
Conversation
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
a3db1a9
to
7e2b9a6
Compare
1b8280e
to
e6c3e18
Compare
7e2b9a6
to
a75f17b
Compare
e6c3e18
to
fbdc226
Compare
a75f17b
to
2ff9768
Compare
fbdc226
to
73a49b2
Compare
25ac177
to
c308621
Compare
73a49b2
to
c26eb39
Compare
17d92ae
to
6bfccae
Compare
c26eb39
to
6008f4b
Compare
6bfccae
to
01152d3
Compare
6008f4b
to
35d81db
Compare
81fa65b
to
df203aa
Compare
9af27b6
to
6e19b77
Compare
df203aa
to
75548a3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AvivYossef-starkware reviewed 2 of 5 files at r1, all commit messages.
Reviewable status: 2 of 5 files reviewed, 3 unresolved discussions (waiting on @noaov1)
crates/blockifier/src/execution/casm_hash_estimation.rs
line 227 at r1 (raw file):
pub const BASE_RANGE_CHECK_NON_EMPTY: usize = 3; // Empty input steps. pub const STEPS_EMPTY_INPUT: usize = 170;
move those const under impl CasmV2HashResourceEstimate
instead of new mode
Code quote:
// Per-felt step cost (measured).
pub const STEPS_BIG_FELT: usize = 45;
pub const STEPS_SMALL_FELT: usize = 15;
// One-time overhead.
// Overhead when input fills a full Blake message (16 u32s).
pub const BASE_STEPS_FULL_MSG: usize = 217;
// Overhead when input results in a partial message (remainder < 16 u32s).
pub const BASE_STEPS_PARTIAL_MSG: usize = 195;
// Extra steps per 2-u32 remainder in partial messages.
pub const STEPS_PER_2_U32_REMINDER: usize = 3;
// Overhead when input for `encode_felt252_data_and_calc_blake_hash` is non-empty.
pub const BASE_RANGE_CHECK_NON_EMPTY: usize = 3;
// Empty input steps.
pub const STEPS_EMPTY_INPUT: usize = 170;
crates/blockifier/src/execution/casm_hash_estimation.rs
line 230 at r1 (raw file):
} fn base_steps_for_blake_hash(n_u32s: usize) -> usize {
plz doc what the function does
Code quote:
base_steps_for_blake_hash
crates/blockifier/src/execution/casm_hash_estimation.rs
line 242 at r1 (raw file):
} fn felts_steps(n_big_felts: usize, n_small_felts: usize) -> usize {
this function should get FeltSizeCount is it happen in a later PR?
Code quote:
felts_steps
crates/blockifier/src/execution/casm_hash_estimation.rs
line 242 at r1 (raw file):
} fn felts_steps(n_big_felts: usize, n_small_felts: usize) -> usize {
plz doc and cosider renaming
Code quote:
felts_steps
crates/blockifier/src/execution/casm_hash_estimation.rs
line 329 at r1 (raw file):
Some(blake_opcode_gas), ) }
move everything under impl CasmV2HashResourceEstimate
Code quote:
// Constants used for estimating the cost of BLAKE hashing inside Starknet OS.
// These values are based on empirical measurement by running
// `encode_felt252_data_and_calc_blake_hash` on various combinations of big and small felts.
mod blake_estimation {
// Per-felt step cost (measured).
pub const STEPS_BIG_FELT: usize = 45;
pub const STEPS_SMALL_FELT: usize = 15;
// One-time overhead.
// Overhead when input fills a full Blake message (16 u32s).
pub const BASE_STEPS_FULL_MSG: usize = 217;
// Overhead when input results in a partial message (remainder < 16 u32s).
pub const BASE_STEPS_PARTIAL_MSG: usize = 195;
// Extra steps per 2-u32 remainder in partial messages.
pub const STEPS_PER_2_U32_REMINDER: usize = 3;
// Overhead when input for `encode_felt252_data_and_calc_blake_hash` is non-empty.
pub const BASE_RANGE_CHECK_NON_EMPTY: usize = 3;
// Empty input steps.
pub const STEPS_EMPTY_INPUT: usize = 170;
}
fn base_steps_for_blake_hash(n_u32s: usize) -> usize {
let rem_u32s = n_u32s % FeltSizeCount::U32_WORDS_PER_MESSAGE;
if rem_u32s == 0 {
blake_estimation::BASE_STEPS_FULL_MSG
} else {
// This computation is based on running blake2s with different inputs.
// Note: all inputs expand to an even number of u32s --> `rem_u32s` is always even.
blake_estimation::BASE_STEPS_PARTIAL_MSG
+ (rem_u32s / 2) * blake_estimation::STEPS_PER_2_U32_REMINDER
}
}
fn felts_steps(n_big_felts: usize, n_small_felts: usize) -> usize {
let big_steps = n_big_felts
.checked_mul(blake_estimation::STEPS_BIG_FELT)
.expect("Overflow computing big felt steps");
let small_steps = n_small_felts
.checked_mul(blake_estimation::STEPS_SMALL_FELT)
.expect("Overflow computing small felt steps");
big_steps.checked_add(small_steps).expect("Overflow computing total felt steps")
}
/// Estimates the number of VM steps needed to hash the given felts with Blake in Starknet OS.
/// Each small felt unpacks into 2 u32s, and each big felt into 8 u32s.
/// Adds a base cost depending on whether the total fits exactly into full 16-u32 messages.
fn estimate_steps_of_encode_felt252_data_and_calc_blake_hash(
felt_size_groups: &FeltSizeCount,
) -> usize {
let total_u32s = felt_size_groups.encoded_u32_len();
if total_u32s == 0 {
// The empty input case is a special case.
return blake_estimation::STEPS_EMPTY_INPUT;
}
let base_steps = base_steps_for_blake_hash(total_u32s);
let felt_steps = felts_steps(felt_size_groups.large, felt_size_groups.small);
base_steps.checked_add(felt_steps).expect("Overflow computing total Blake hash steps")
}
/// Estimates resource usage for `encode_felt252_data_and_calc_blake_hash` in the Starknet OS.
///
/// # Encoding Details
/// - Small felts → 2 `u32`s each; Big felts → 8 `u32`s each.
/// - Each felt requires one `range_check` operation.
///
/// # Returns:
/// - `ExecutionResources`: VM resource usage (e.g., n_steps, range checks).
/// - `usize`: number of Blake opcodes used, accounted for separately as those are not reported via
/// `ExecutionResources`.
pub fn encode_and_blake_hash_resources(
felt_size_groups: &FeltSizeCount,
) -> EstimatedExecutionResources {
let n_steps = estimate_steps_of_encode_felt252_data_and_calc_blake_hash(felt_size_groups);
let builtin_instance_counter = match felt_size_groups.n_felts() {
// The empty case does not use builtins at all.
0 => HashMap::new(),
// One `range_check` per input felt to validate its size + Overhead for the non empty case.
_ => HashMap::from([(
BuiltinName::range_check,
felt_size_groups.n_felts() + blake_estimation::BASE_RANGE_CHECK_NON_EMPTY,
)]),
};
let resources = ExecutionResources { n_steps, n_memory_holes: 0, builtin_instance_counter };
EstimatedExecutionResources::V2Hash {
resources,
blake_count: felt_size_groups.blake_opcode_count(),
}
}
/// Converts the execution resources and blake opcode count to L2 gas.
///
/// Used for both Stwo ("proving_gas") and Stone ("sierra_gas") estimations, which differ in
/// builtin costs. This unified logic is valid because only the `range_check` builtin is used,
/// and its cost is identical across provers (see `bouncer.get_tx_weights`).
// TODO(AvivG): Move inside blake estimation struct.
pub fn blake_execution_resources_estimation_to_gas(
resources: EstimatedExecutionResources,
versioned_constants: &VersionedConstants,
blake_opcode_gas: usize,
) -> GasAmount {
// TODO(AvivG): Remove this once gas computation is separated from resource estimation.
assert!(
resources
.resources()
.builtin_instance_counter
.keys()
.all(|&k| k == BuiltinName::range_check),
"Expected either empty builtins or only `range_check` builtin, got: {:?}. This breaks the \
assumption that builtin costs are identical between provers.",
resources.resources().builtin_instance_counter.keys().collect::<Vec<_>>()
);
resources.to_sierra_gas(
|resources| vm_resources_to_sierra_gas(resources, versioned_constants),
Some(blake_opcode_gas),
)
}
crates/blockifier/src/execution/casm_hash_estimation_test.rs
line 140 at r1 (raw file):
assert_eq!(resources.resources(), &expected, "Unexpected resources values for zero-input hash"); assert_eq!(resources.blake_count(), 0, "Expected zero BLAKE opcodes for zero inputs"); }
Out of scope move it to different PR
Code quote:
#[test]
fn test_u32_constants() {
// Small value < 2^63, will encode to 2 u32s.
let small_felt = Felt::ONE;
// Large value >= 2^63, will encode to 8 u32s (Just above 2^63).
let big_felt = Felt::from_hex_unchecked("8000000000000001");
let small_u32s = encode_felts_to_u32s(vec![small_felt]);
let big_u32s = encode_felts_to_u32s(vec![big_felt]);
// Blake estimation constants should match the actual encoding.
assert_eq!(small_u32s.len(), FeltSizeCount::U32_WORDS_PER_SMALL_FELT);
assert_eq!(big_u32s.len(), FeltSizeCount::U32_WORDS_PER_LARGE_FELT);
}
/// Test the edge case of hashing an empty array of felt values.
#[test]
fn test_zero_inputs() {
// logic was written.
let steps = estimate_steps_of_encode_felt252_data_and_calc_blake_hash(&FeltSizeCount {
large: 0,
small: 0,
});
assert_eq!(steps, STEPS_EMPTY_INPUT, "Unexpected base step cost for zero inputs");
// No opcodes should be emitted.
let opcodes = FeltSizeCount::default().blake_opcode_count();
assert_eq!(opcodes, 0, "Expected zero BLAKE opcodes for zero inputs");
// Should result in base cost only (no opcode cost).
let resources = encode_and_blake_hash_resources(&FeltSizeCount::default());
let expected = ExecutionResources { n_steps: STEPS_EMPTY_INPUT, ..Default::default() };
assert_eq!(resources.resources(), &expected, "Unexpected resources values for zero-input hash");
assert_eq!(resources.blake_count(), 0, "Expected zero BLAKE opcodes for zero inputs");
}
No description provided.