Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.
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
4 changes: 2 additions & 2 deletions bench/internals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use starknet_rs::{
internal_invoke_function::InternalInvokeFunction,
},
},
core::contract_address::starknet_contract_address::compute_class_hash,
core::contract_address::starknet_contract_address::compute_deprecated_class_hash,
definitions::general_config::StarknetChainId,
public::abi::VALIDATE_ENTRY_POINT_SELECTOR,
services::api::contract_class::ContractClass,
Expand All @@ -27,7 +27,7 @@ lazy_static! {
"starknet_programs/account_without_validation.json",
))
.unwrap();
static ref CLASS_HASH: [u8; 32] = felt_to_hash(&compute_class_hash(
static ref CLASS_HASH: [u8; 32] = felt_to_hash(&compute_deprecated_class_hash(
&CONTRACT_CLASS
).unwrap());
static ref CONTRACT_ADDRESS: Address = Address(felt_str!(
Expand Down
263 changes: 263 additions & 0 deletions cairo_programs/compiled_class.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.cairo_builtins import PoseidonBuiltin
from starkware.cairo.common.hash_state_poseidon import (
HashState,
hash_finalize,
hash_init,
hash_update_single,
hash_update_with_nested_hash,
)
from starkware.cairo.common.math import assert_lt_felt
from starkware.cairo.common.registers import get_fp_and_pc

const COMPILED_CLASS_VERSION = 'COMPILED_CLASS_V1';

struct CompiledClassEntryPoint {
// A field element that encodes the signature of the called function.
selector: felt,
// The offset of the instruction that should be called within the contract bytecode.
offset: felt,
// The number of builtins in 'builtin_list'.
n_builtins: felt,
// 'builtin_list' is a continuous memory segment containing the ASCII encoding of the (ordered)
// builtins used by the function.
builtin_list: felt*,
}

struct CompiledClass {
compiled_class_version: felt,

// The length and pointer to the external entry point table of the contract.
n_external_functions: felt,
external_functions: CompiledClassEntryPoint*,

// The length and pointer to the L1 handler entry point table of the contract.
n_l1_handlers: felt,
l1_handlers: CompiledClassEntryPoint*,

// The length and pointer to the constructor entry point table of the contract.
n_constructors: felt,
constructors: CompiledClassEntryPoint*,

// The hinted_compiled_class_hash field should be set to the starknet_keccak of the
// contract program, including its hints. However the OS does not validate that.
// This field may be used by the operator to differentiate between contract classes that
// differ only in the hints.
// This field is included in the hash of the CompiledClass to simplify the implementation.
hinted_compiled_class_hash: felt,

// The length and pointer of the bytecode.
bytecode_length: felt,
bytecode_ptr: felt*,
}

// Checks that the list of selectors is sorted.
func validate_entry_points{range_check_ptr}(
n_entry_points: felt, entry_points: CompiledClassEntryPoint*
) {
if (n_entry_points == 0) {
return ();
}

return validate_entry_points_inner(
n_entry_points=n_entry_points - 1,
entry_points=&entry_points[1],
prev_selector=entry_points[0].selector,
);
}

// Inner function for validate_entry_points.
func validate_entry_points_inner{range_check_ptr}(
n_entry_points: felt, entry_points: CompiledClassEntryPoint*, prev_selector
) {
if (n_entry_points == 0) {
return ();
}

assert_lt_felt(prev_selector, entry_points[0].selector);

return validate_entry_points_inner(
n_entry_points=n_entry_points - 1,
entry_points=&entry_points[1],
prev_selector=entry_points[0].selector,
);
}

func compiled_class_hash{poseidon_ptr: PoseidonBuiltin*}(compiled_class: CompiledClass*) -> (
hash: felt
) {
let hash_state: HashState = hash_init();
with hash_state {
hash_update_single(item=compiled_class.compiled_class_version);

// Hash external entry points.
hash_entry_points(
entry_points=compiled_class.external_functions,
n_entry_points=compiled_class.n_external_functions,
);

// Hash L1 handler entry points.
hash_entry_points(
entry_points=compiled_class.l1_handlers, n_entry_points=compiled_class.n_l1_handlers
);

// Hash constructor entry points.
hash_entry_points(
entry_points=compiled_class.constructors, n_entry_points=compiled_class.n_constructors
);

// Hash hinted_compiled_class_hash.
hash_update_single(item=compiled_class.hinted_compiled_class_hash);

// Hash bytecode.
hash_update_with_nested_hash(
data_ptr=compiled_class.bytecode_ptr, data_length=compiled_class.bytecode_length
);
}

let hash: felt = hash_finalize(hash_state=hash_state);
return (hash=hash);
}

func hash_entry_points{poseidon_ptr: PoseidonBuiltin*, hash_state: HashState}(
entry_points: CompiledClassEntryPoint*, n_entry_points: felt
) {
let inner_hash_state = hash_init();
hash_entry_points_inner{hash_state=inner_hash_state}(
entry_points=entry_points, n_entry_points=n_entry_points
);
let hash: felt = hash_finalize(hash_state=inner_hash_state);
hash_update_single(item=hash);

return ();
}

func hash_entry_points_inner{poseidon_ptr: PoseidonBuiltin*, hash_state: HashState}(
entry_points: CompiledClassEntryPoint*, n_entry_points: felt
) {
if (n_entry_points == 0) {
return ();
}

hash_update_single(item=entry_points.selector);
hash_update_single(item=entry_points.offset);

// Hash builtins.
hash_update_with_nested_hash(
data_ptr=entry_points.builtin_list, data_length=entry_points.n_builtins
);

return hash_entry_points_inner(
entry_points=&entry_points[1], n_entry_points=n_entry_points - 1
);
}

// A list entry that maps a hash to the corresponding contract classes.
struct CompiledClassFact {
// The hash of the contract. This member should be first, so that we can lookup items
// with the hash as key, using find_element().
hash: felt,
compiled_class: CompiledClass*,
}

// Loads the contract classes from the 'os_input' hint variable.
// Returns CompiledClassFact list that maps a hash to a CompiledClass.
func load_compiled_class_facts{poseidon_ptr: PoseidonBuiltin*, range_check_ptr}() -> (
n_compiled_class_facts: felt, compiled_class_facts: CompiledClassFact*
) {
alloc_locals;
local n_compiled_class_facts;
local compiled_class_facts: CompiledClassFact*;
%{
ids.compiled_class_facts = segments.add()
ids.n_compiled_class_facts = len(os_input.compiled_classes)
vm_enter_scope({
'compiled_class_facts': iter(os_input.compiled_classes.items()),
})
%}

let (builtin_costs: felt*) = alloc();
assert builtin_costs[0] = 0;
assert builtin_costs[1] = 0;
assert builtin_costs[2] = 0;
assert builtin_costs[3] = 0;
assert builtin_costs[4] = 0;

load_compiled_class_facts_inner(
n_compiled_class_facts=n_compiled_class_facts,
compiled_class_facts=compiled_class_facts,
builtin_costs=builtin_costs,
);
%{ vm_exit_scope() %}

return (
n_compiled_class_facts=n_compiled_class_facts, compiled_class_facts=compiled_class_facts
);
}

// Loads 'n_compiled_class_facts' from the hint 'compiled_class_facts' and appends the
// corresponding CompiledClassFact to compiled_class_facts.
func load_compiled_class_facts_inner{poseidon_ptr: PoseidonBuiltin*, range_check_ptr}(
n_compiled_class_facts, compiled_class_facts: CompiledClassFact*, builtin_costs: felt*
) {
if (n_compiled_class_facts == 0) {
return ();
}
alloc_locals;

let compiled_class_fact = compiled_class_facts[0];
let compiled_class = compiled_class_fact.compiled_class;

// Fetch contract data form hints.
%{
from starkware.starknet.core.os.contract_class.compiled_class_hash import (
get_compiled_class_struct,
)

compiled_class_hash, compiled_class = next(compiled_class_facts)

cairo_contract = get_compiled_class_struct(
identifiers=ids._context.identifiers, compiled_class=compiled_class)
ids.compiled_class = segments.gen_arg(cairo_contract)
%}

assert compiled_class.compiled_class_version = COMPILED_CLASS_VERSION;

validate_entry_points(
n_entry_points=compiled_class.n_external_functions,
entry_points=compiled_class.external_functions,
);

validate_entry_points(
n_entry_points=compiled_class.n_l1_handlers, entry_points=compiled_class.l1_handlers
);

let (hash) = compiled_class_hash(compiled_class);
compiled_class_fact.hash = hash;

// Compiled classes are expected to end with a `ret` opcode followed by a pointer to the
// builtin costs.
assert compiled_class.bytecode_ptr[compiled_class.bytecode_length] = 0x208b7fff7fff7ffe;
assert compiled_class.bytecode_ptr[compiled_class.bytecode_length + 1] = cast(
builtin_costs, felt
);

%{
computed_hash = ids.compiled_class_fact.hash
expected_hash = compiled_class_hash
assert computed_hash == expected_hash, (
"Computed compiled_class_hash is inconsistent with the hash in the os_input. "
f"Computed hash = {computed_hash}, Expected hash = {expected_hash}.")

vm_load_program(
compiled_class.get_runnable_program(entrypoint_builtins=[]),
ids.compiled_class.bytecode_ptr
)
%}

return load_compiled_class_facts_inner(
n_compiled_class_facts=n_compiled_class_facts - 1,
compiled_class_facts=compiled_class_facts + CompiledClassFact.SIZE,
builtin_costs=builtin_costs,
);
}
77 changes: 77 additions & 0 deletions cairo_programs/contract_class.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from starkware.cairo.common.cairo_builtins import PoseidonBuiltin
from starkware.cairo.common.hash_state_poseidon import (
HashState,
hash_finalize,
hash_init,
hash_update_single,
hash_update_with_nested_hash,
)

const CONTRACT_CLASS_VERSION = 'CONTRACT_CLASS_V0.1.0';

struct ContractEntryPoint {
// A field element that encodes the signature of the called function.
selector: felt,
function_idx: felt,
}

struct ContractClass {
contract_class_version: felt,

// The length and pointer to the external entry point table of the contract.
n_external_functions: felt,
external_functions: ContractEntryPoint*,

// The length and pointer to the L1 handler entry point table of the contract.
n_l1_handlers: felt,
l1_handlers: ContractEntryPoint*,

// The length and pointer to the constructor entry point table of the contract.
n_constructors: felt,
constructors: ContractEntryPoint*,

// starknet_keccak of the contract ABI.
// Note that the OS does not enforce any constraints on this value.
abi_hash: felt,

// The length and pointer of the Sierra program.
sierra_program_length: felt,
sierra_program_ptr: felt*,
}

func class_hash{poseidon_ptr: PoseidonBuiltin*}(contract_class: ContractClass*) -> (hash: felt) {
let hash_state: HashState = hash_init();
with hash_state {
hash_update_single(item=contract_class.contract_class_version);

// Hash external entry points.
hash_update_with_nested_hash(
data_ptr=contract_class.external_functions,
data_length=contract_class.n_external_functions * ContractEntryPoint.SIZE,
);

// Hash L1 handler entry points.
hash_update_with_nested_hash(
data_ptr=contract_class.l1_handlers,
data_length=contract_class.n_l1_handlers * ContractEntryPoint.SIZE,
);

// Hash constructor entry points.
hash_update_with_nested_hash(
data_ptr=contract_class.constructors,
data_length=contract_class.n_constructors * ContractEntryPoint.SIZE,
);

// Hash abi_hash.
hash_update_single(item=contract_class.abi_hash);

// Hash Sierra program.
hash_update_with_nested_hash(
data_ptr=contract_class.sierra_program_ptr,
data_length=contract_class.sierra_program_length,
);

let hash: felt = hash_finalize(hash_state=hash_state);
}
return (hash=hash);
}
Loading