Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ num-traits = "0.2.19"
regex = "1.11.1"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.142"
starknet-crypto = "0.6.2"
starknet-crypto = "0.7.4"
starknet-types-core = "0.1.2"
stwo_cairo_utils = { git = "https://github.com/starkware-libs/stwo-cairo", rev = "17a1b4cead265fe0312ef5bda771b12b5472d1c8" }
stwo-cairo-adapter = { git = "https://github.com/starkware-libs/stwo-cairo", rev = "17a1b4cead265fe0312ef5bda771b12b5472d1c8" }
Expand Down
18 changes: 6 additions & 12 deletions crates/cairo-program-runner-lib/src/hints/builtin_usage_hints.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashMap;

use super::{
execute_task_hints::field_element_to_felt, fact_topologies::GPS_FACT_TOPOLOGY,
execute_task_hints::felt_to_felt252, fact_topologies::GPS_FACT_TOPOLOGY,
types::FlexibleBuiltinUsageInput, PROGRAM_INPUT,
};
use cairo_vm::{
Expand All @@ -16,7 +16,7 @@ use cairo_vm::{
vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
Felt252,
};
use starknet_crypto::{pedersen_hash, FieldElement};
use starknet_crypto::{pedersen_hash, Felt};

/// Implements hint:
/// %{
Expand Down Expand Up @@ -97,11 +97,8 @@ pub fn builtin_usage_set_pages_and_fact_topology(
) -> Result<(), HintError> {
let output_ptr = get_ptr_from_var_name("output_ptr", vm, ids_data, ap_tracking)?;
let output_ptr_val = vm.get_integer(output_ptr)?.into_owned();
let ped_hash_val = pedersen_hash(
&FieldElement::from(123_usize),
&FieldElement::from(456_usize),
);
if output_ptr_val != field_element_to_felt(ped_hash_val) {
let ped_hash_val = pedersen_hash(&Felt::from(123_usize), &Felt::from(456_usize));
if output_ptr_val != felt_to_felt252(ped_hash_val) {
return Err(HintError::CustomHint(
format!("Pedersen hash mismatch: expected {ped_hash_val}, got {output_ptr_val}").into(),
));
Expand Down Expand Up @@ -425,11 +422,8 @@ mod tests {
vm.builtin_runners = vec![output_builtin_runner.into()];

// Load pedersen hash into output builtin_ptr
let ped_hash = pedersen_hash(
&FieldElement::from(left_pedersen_hash),
&FieldElement::from(456_usize),
);
let ped_hash_felt = field_element_to_felt(ped_hash);
let ped_hash = pedersen_hash(&Felt::from(left_pedersen_hash), &Felt::from(456_usize));
let ped_hash_felt = felt_to_felt252(ped_hash);
let _ = vm
.load_data(
Relocatable::from((2, 0)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use cairo_vm::vm::runners::builtin_runner::{OutputBuiltinRunner, OutputBuiltinSt
use cairo_vm::vm::runners::cairo_pie::CairoPie;
use cairo_vm::vm::vm_core::VirtualMachine;
use cairo_vm::{any_box, Felt252};
use starknet_crypto::FieldElement;
use starknet_crypto::Felt;

use super::types::HashFunc;
use super::utils::{get_identifier, get_program_from_task, get_program_identifies};
Expand All @@ -33,7 +33,7 @@ use crate::hints::program_loader::ProgramLoader;
use crate::hints::types::{BootloaderVersion, Task};
use crate::hints::vars;

pub fn field_element_to_felt(field_element: FieldElement) -> Felt252 {
pub fn felt_to_felt252(field_element: Felt) -> Felt252 {
let bytes = field_element.to_bytes_be();
Felt252::from_bytes_be(&bytes)
}
Expand Down Expand Up @@ -175,7 +175,7 @@ pub fn validate_hash(
.map_err(|e| {
HintError::CustomHint(format!("Could not compute program hash: {e}").into_boxed_str())
})?;
let computed_program_hash = field_element_to_felt(computed_program_hash);
let computed_program_hash = felt_to_felt252(computed_program_hash);

if program_hash != computed_program_hash {
return Err(HintError::AssertionFailed(
Expand Down Expand Up @@ -543,7 +543,7 @@ pub fn bootloader_validate_hash(
.map_err(|e| {
HintError::CustomHint(format!("Could not compute program hash: {e}").into_boxed_str())
})?;
let computed_program_hash = field_element_to_felt(computed_program_hash);
let computed_program_hash = felt_to_felt252(computed_program_hash);
if program_hash != computed_program_hash {
return Err(HintError::AssertionFailed(
"Computed hash does not match input"
Expand Down
87 changes: 40 additions & 47 deletions crates/cairo-program-runner-lib/src/hints/program_hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use cairo_vm::types::builtin_name::BuiltinName;
use cairo_vm::types::relocatable::MaybeRelocatable;
use cairo_vm::vm::runners::cairo_pie::StrippedProgram;
use cairo_vm::Felt252;
use starknet_crypto::{pedersen_hash, poseidon_hash_many, FieldElement};
use starknet_crypto::{pedersen_hash, poseidon_hash_many, Felt};

use super::types::HashFunc;

type HashFunction = fn(&FieldElement, &FieldElement) -> FieldElement;
type HashFunction = fn(&Felt, &Felt) -> Felt;

#[derive(thiserror_no_std::Error, Debug)]
pub enum HashChainError {
Expand All @@ -27,10 +27,10 @@ pub enum ProgramHashError {
#[error("Invalid program data: data contains relocatable(s)")]
InvalidProgramData,

/// Conversion from Felt252 to FieldElement failed. This is unlikely to happen
/// Conversion from Felt252 to Felt failed. This is unlikely to happen
/// unless the implementation of Felt252 changes and this code is not updated properly.
#[error("Conversion from Felt252 to FieldElement failed")]
Felt252ToFieldElementConversionFailed,
#[error("Conversion from Felt252 to Felt failed")]
Felt252ToFeltConversionFailed,
}
/*
Computes a hash chain over the data, in the following order:
Expand All @@ -41,50 +41,54 @@ pub enum ProgramHashError {
assert len(data) >= 1, f"len(data) for hash chain computation must be >= 1; got: {len(data)}."
return functools.reduce(lambda x, y: hash_func(y, x), data[::-1])
*/
fn compute_hash_chain<'a, I>(
data: I,
hash_func: HashFunction,
) -> Result<FieldElement, HashChainError>
fn compute_hash_chain<'a, I>(data: I, hash_func: HashFunction) -> Result<Felt, HashChainError>
where
I: Iterator<Item = &'a FieldElement> + DoubleEndedIterator,
I: Iterator<Item = &'a Felt> + DoubleEndedIterator,
{
match data.copied().rev().reduce(|x, y| hash_func(&y, &x)) {
Some(result) => Ok(result),
None => Err(HashChainError::EmptyData),
}
}

/// Creates an instance of `FieldElement` from a builtin name.
/// Creates an instance of `Felt` from a builtin name.
///
/// Converts the builtin name to bytes then attempts to create a field element from
/// these bytes. This function will fail if the builtin name is over 31 characters.
fn builtin_to_field_element(builtin: &BuiltinName) -> Result<FieldElement, ProgramHashError> {
fn builtin_to_felt(builtin: &BuiltinName) -> Result<Felt, ProgramHashError> {
// The Python implementation uses the builtin name without suffix
let builtin_name = builtin.to_str();

FieldElement::from_byte_slice_be(builtin_name.as_bytes())
.map_err(|_| ProgramHashError::InvalidProgramBuiltin(builtin.to_str()))
// TODO(idanh): not sure if this check is correct, documentation of Felt::from_bytes_be_slice
// works in chunks of 32 bytes and not 31...
if builtin_name.len() > 31 {
return Err(ProgramHashError::InvalidProgramBuiltin(builtin.to_str()));
}
Ok(Felt::from_bytes_be_slice(builtin_name.as_bytes()))
}

/// The `value: FieldElement` is `pub(crate)` and there is no accessor.
/// This function converts a `Felt252` to a `FieldElement` using a safe, albeit inefficient,
/// The `value: Felt` is `pub(crate)` and there is no accessor.
/// This function converts a `Felt252` to a `Felt` using a safe, albeit inefficient,
/// method.
fn felt_to_field_element(felt: &Felt252) -> Result<FieldElement, ProgramHashError> {
fn felt252_to_felt(felt: &Felt252) -> Result<Felt, ProgramHashError> {
let bytes = felt.to_bytes_be();
FieldElement::from_bytes_be(&bytes)
.map_err(|_e| ProgramHashError::Felt252ToFieldElementConversionFailed)
// Check if bytes length is over 31
if bytes.len() > 32 {
return Err(ProgramHashError::Felt252ToFeltConversionFailed);
}
Ok(Felt::from_bytes_be(&bytes))
}

/// Converts a `MaybeRelocatable` into a `FieldElement` value.
/// Converts a `MaybeRelocatable` into a `Felt` value.
///
/// Returns `InvalidProgramData` if `maybe_relocatable` is not an integer
fn maybe_relocatable_to_field_element(
fn maybe_relocatable_to_felt(
maybe_relocatable: &MaybeRelocatable,
) -> Result<FieldElement, ProgramHashError> {
) -> Result<Felt, ProgramHashError> {
let felt = maybe_relocatable
.get_int_ref()
.ok_or(ProgramHashError::InvalidProgramData)?;
felt_to_field_element(felt)
felt252_to_felt(felt)
}

#[allow(dead_code)] // TODO: remove
Expand All @@ -103,33 +107,26 @@ pub fn compute_program_hash_chain(
program: &StrippedProgram,
bootloader_version: usize,
program_hash_function: HashFunc,
) -> Result<FieldElement, ProgramHashError> {
) -> Result<Felt, ProgramHashError> {
let program_main = program.main;
let program_main = FieldElement::from(program_main);
let program_main = Felt::from(program_main);

// Convert builtin names to field elements
let builtin_list: Result<Vec<FieldElement>, _> = program
.builtins
.iter()
.map(builtin_to_field_element)
.collect();
let builtin_list: Result<Vec<Felt>, _> = program.builtins.iter().map(builtin_to_felt).collect();
let builtin_list = builtin_list?;

let program_header = vec![
FieldElement::from(bootloader_version),
Felt::from(bootloader_version),
program_main,
FieldElement::from(program.builtins.len()),
Felt::from(program.builtins.len()),
];

let program_data: Result<Vec<_>, _> = program
.data
.iter()
.map(maybe_relocatable_to_field_element)
.collect();
let program_data: Result<Vec<_>, _> =
program.data.iter().map(maybe_relocatable_to_felt).collect();
let program_data = program_data?;

let data_chain_len = program_header.len() + builtin_list.len() + program_data.len();
let data_chain_len_vec = vec![FieldElement::from(data_chain_len)];
let data_chain_len_vec = vec![Felt::from(data_chain_len)];

// Prepare a chain of iterators to feed to the hash function
let data_chain = [
Expand All @@ -144,14 +141,14 @@ pub fn compute_program_hash_chain(
compute_hash_chain(data_chain.iter().flat_map(|&v| v.iter()), pedersen_hash)?
}
HashFunc::Poseidon => {
let data: Vec<FieldElement> = data_chain[1..]
let data: Vec<Felt> = data_chain[1..]
.iter()
.flat_map(|&v| v.iter().copied())
.collect();
poseidon_hash_many(&data)
}
// TODO(yairv): replace dummy with actual impl.
HashFunc::Blake => FieldElement::from(0x123456789u64),
HashFunc::Blake => Felt::from(0x123456789u64),
};
Ok(hash)
}
Expand All @@ -168,14 +165,10 @@ mod tests {

#[test]
fn test_compute_hash_chain() {
let data: Vec<FieldElement> = vec![
FieldElement::from(1u64),
FieldElement::from(2u64),
FieldElement::from(3u64),
];
let data: Vec<Felt> = vec![Felt::from(1u64), Felt::from(2u64), Felt::from(3u64)];
let expected_hash = pedersen_hash(
&FieldElement::from(1u64),
&pedersen_hash(&FieldElement::from(2u64), &FieldElement::from(3u64)),
&Felt::from(1u64),
&pedersen_hash(&Felt::from(2u64), &Felt::from(3u64)),
);
let computed_hash = compute_hash_chain(data.iter(), pedersen_hash)
.expect("Hash computation failed unexpectedly");
Expand Down