Skip to content

starknet_os: test blake v0.14 #8045

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

Open
wants to merge 1 commit into
base: aviv/fix_len_0
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,10 @@ func blake2s_felts{range_check_ptr, bitwise_ptr: BitwiseBuiltin*, blake2s_ptr: f
// Note: This function can nondeterministically choose between several encodings of felts,
// x < PRIME can be encoded as x + PRIME, x + 2 * PRIME, etc. The canonical encoding is
// given when x < PRIME.
// TODO(alont): Write custom hints and integrate with VM.
// TODO(alont): Consider adding cases for 1 u32 (small immediates, including negatives)
// and 3 u32s (extended opcodes).
// TODO(alont): Consider unrolling this loop to avoid state copy overhead.
func encode_felt252_to_u32s{range_check_ptr: felt}(
packed_values_len: felt, packed_values: felt*, unpacked_u32s: felt*
) -> felt {
Expand Down Expand Up @@ -667,23 +671,8 @@ const BLAKE2S_FINALIZE_INSTRUCTION = OFF_MINUS_1 * COUNTER_OFFSET + OFF_MINUS_3
OPCODE_EXT_OFFSET;

// Computes blake2s of `input` of size `len` felts, representing 32 bits each.
// Note: this function guarantees that len > 0.
func blake_with_opcode{range_check_ptr}(len: felt, data: felt*, out: felt*) {
alloc_locals;
if (len == 0) {
// hash32 = [105,33,122,48, 121,144,128,148, 225,17,33,208, 66,53,74,124,
// 31,85,182,72, 44,161,165,30, 27,37,13,253, 30,208,238,249]
// as little-endian u32s:
assert [out + 0] = 813310313; // 0x307A2169
assert [out + 1] = 2491453561; // 0x94809079
assert [out + 2] = 3491828193; // 0xD02111E1
assert [out + 3] = 2085238082; // 0x7C4A3542
assert [out + 4] = 1219908895; // 0x48B6551F
assert [out + 5] = 514171180; // 0x1EA5A12C
assert [out + 6] = 4245497115; // 0xFD0D251B
assert [out + 7] = 4193177630; // 0xF9EED01E
return ();
}

let (local state: felt*) = alloc();
assert state[0] = 0x6B08E647; // IV[0] ^ 0x01010020 (config: no key, 32 bytes output).
Expand All @@ -696,8 +685,16 @@ func blake_with_opcode{range_check_ptr}(len: felt, data: felt*, out: felt*) {
assert state[7] = 0x5BE0CD19;

// Express the length in bytes, subtract the remainder for finalize.
let (_, rem) = unsigned_div_rem(len - 1, 16);
local rem = rem + 1;
local rem;
if (len == 0) {
assert rem = 0;
tempvar range_check_ptr = range_check_ptr;
} else {
let (_, r) = unsigned_div_rem(len - 1, 16);
assert rem = r + 1;
tempvar range_check_ptr = range_check_ptr;
}

local len_in_bytes = (len - rem) * 4;

local range_check_ptr = range_check_ptr;
Expand Down Expand Up @@ -745,8 +742,8 @@ func blake_with_opcode{range_check_ptr}(len: felt, data: felt*, out: felt*) {

// Given `data_len` felt252s at `data`, encodes them as u32s as defined in `encode_felt252_to_u32s`
// and computes the blake2s hash of the result using the dedicated opcodes.
// The result is then returned as a 224-bit felt, ignoring the last 32 bits.
func encode_felt252_data_and_calc_224_bit_blake_hash{range_check_ptr: felt}(
// The 256 bit result is then returned as a felt252 (i.e. modulo PRIME).
func encode_felt252_data_and_calc_blake_hash{range_check_ptr: felt}(
data_len: felt, data: felt*
) -> (hash: felt) {
alloc_locals;
Expand All @@ -757,8 +754,8 @@ func encode_felt252_data_and_calc_224_bit_blake_hash{range_check_ptr: felt}(
let (local blake_output: felt*) = alloc();
blake_with_opcode(len=encoded_data_len, data=encoded_data, out=blake_output);
return (
hash=blake_output[6] * 2 ** 192 + blake_output[5] * 2 ** 160 + blake_output[4] * 2 ** 128 +
blake_output[3] * 2 ** 96 + blake_output[2] * 2 ** 64 + blake_output[1] * 2 ** 32 +
blake_output[0],
hash=blake_output[7] * 2 ** 224 + blake_output[6] * 2 ** 192 + blake_output[5] * 2 ** 160 +
blake_output[4] * 2 ** 128 + blake_output[3] * 2 ** 96 + blake_output[2] * 2 ** 64 +
blake_output[1] * 2 ** 32 + blake_output[0],
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ from starkware.starknet.core.os.output import (
)
from starkware.starknet.core.os.state.commitment import StateEntry
from starkware.starknet.core.os.state.state import OsStateUpdate, state_update
from starkware.starknet.core.os.hash.blake2s import encode_felt252_data_and_calc_224_bit_blake_hash
from starkware.starknet.core.os.hash.blake2s import encode_felt252_data_and_calc_blake_hash

// Executes transactions on Starknet.
func main{
Expand Down
43 changes: 31 additions & 12 deletions crates/blake2s/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,48 @@ pub fn encode_felts_to_u32s(felts: Vec<Felt>) -> Vec<u32> {
unpacked_u32s
}

/// Packs the first 7 little-endian 32-bit words (28 bytes) of `bytes`
/// into a single 224-bit Felt.
fn pack_224_le_to_felt(bytes: &[u8]) -> Felt {
assert!(bytes.len() >= 28, "need at least 28 bytes to pack 7 words");
/// Packs the first 8 little-endian 32-bit words (32 bytes) of `bytes` into a single long Felt
/// by summing each word shifted by multiples of 32 bits:
///
/// `result = word_0 + (word_1 << 32) + (word_2 << 64) + ... + (word_7 << 224) (mod P)`
///
/// # Panics
///
/// Panics if `bytes.len() < 32`.
pub fn pack_256_le_to_felt(bytes: &[u8]) -> Felt {
const BYTES_PER_WORD: usize = 4;
const WORD_COUNT: usize = 8;
assert!(
bytes.len() >= BYTES_PER_WORD * WORD_COUNT,
"pack_224_le_to_felt: need at least {} bytes, got {}",
BYTES_PER_WORD * WORD_COUNT,
bytes.len()
);

// 1) copy your 28-byte LE-hash into the low 28 bytes of a 32-byte buffer
let mut buf = [0u8; 32];
buf[..28].copy_from_slice(&bytes[..28]);
let mut result = Felt::ZERO;
let mut current_factor = Felt::ONE;
let shift_factor = Felt::from(1u64 << 32);

for chunk in bytes[..BYTES_PER_WORD * WORD_COUNT].chunks_exact(BYTES_PER_WORD) {
// Each chunk is exactly 4 bytes, little-endian order
let word = u32::from_le_bytes(chunk.try_into().unwrap());
result += Felt::from(word) * current_factor;
current_factor *= shift_factor;
}

// 2) interpret the whole 32-byte buffer as a little-endian Felt
Felt::from_bytes_le(&buf)
result
}

pub fn blake2s_to_felt(data: &[u8]) -> Felt {
let mut hasher = Blake2s256::new();
hasher.update(data);
let hash32 = hasher.finalize();
pack_224_le_to_felt(hash32.as_slice())
pack_256_le_to_felt(hash32.as_slice())
}

/// Encodes a slice of `Felt` values into 32-bit words exactly as Cairo’s
/// `encode_felt252_to_u32s` hint does, then hashes the resulting byte stream
/// with Blake2s-256 and returns the 224-bit truncated digest as a `Felt`.
/// with Blake2s-256 and returns the 256-bit truncated digest as a `Felt`.
pub fn encode_felt252_data_and_calc_224_bit_blake_hash(data: &[Felt]) -> Felt {
// 1) Unpack each Felt into 2 or 8 u32 limbs
let u32_words = encode_felts_to_u32s(data.to_vec());
Expand All @@ -71,6 +90,6 @@ pub fn encode_felt252_data_and_calc_224_bit_blake_hash(data: &[Felt]) -> Felt {
byte_stream.extend_from_slice(&word.to_le_bytes());
}

// 3) Compute Blake2s-256 over the bytes and pack the first 224 bits into a Felt
// 3) Compute Blake2s-256 over the bytes and pack the first 256 bits into a Felt
blake2s_to_felt(&byte_stream)
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ fn test_cairo_vs_rust_blake2s_implementation(#[case] test_data: Vec<Felt>) {
let result = initialize_and_run_cairo_0_entry_point(
&runner_config,
program_bytes,
"starkware.starknet.core.os.hash.blake2s.encode_felt252_data_and_calc_224_bit_blake_hash",
"starkware.starknet.core.os.hash.blake2s.encode_felt252_data_and_calc_blake_hash",
&explicit_args,
&implicit_args,
&expected_return_values,
Expand Down
Loading