Skip to content

Commit

Permalink
Merge branch 'main' into ft/fix_blocks_less_than_10
Browse files Browse the repository at this point in the history
  • Loading branch information
HermanObst authored Dec 27, 2024
2 parents 6611d38 + 3f5bbc6 commit e4b5e3e
Show file tree
Hide file tree
Showing 16 changed files with 1,772 additions and 270 deletions.
11 changes: 0 additions & 11 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,3 @@ jobs:
cairo-compile cairo-lang/src/starkware/starknet/core/os/os.cairo --output build/os_latest.json --cairo_path cairo-lang/src
- run: cargo install cargo-udeps --locked
- run: cargo udeps --all-targets

orphans:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: rustup show
- run: rustup component add rustfmt
- uses: Swatinem/rust-cache@v2
- run: git submodule update --init
- run: ./scripts/check-orphans.sh

10 changes: 10 additions & 0 deletions .github/workflows/prove_blocks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,13 @@ jobs:
PATHFINDER_RPC_URL: ${{ secrets.PATHFINDER_RPC_URL }}
run: |
cargo test --release --package prove_block --test hash_tests -- test_recompute_class_hash test_class_proof_verification_ok test_class_proof_verification_non_inclusion --show-output --ignored
- name: Hint tool Orphans
env:
PATHFINDER_RPC_URL: ${{ secrets.PATHFINDER_RPC_URL }}
run: |
RESULT=$(cargo run --release -p hint_tool -- --subset orphaned --in-file build/os_latest.json | grep -oP '\d+$')
if [ "$RESULT" -gt 1 ]; then
echo "Error: Only breakpoint hint is allowed to be orphaned."
exit 1
fi
37 changes: 35 additions & 2 deletions crates/bin/prove_block/src/rpc_utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};

use blockifier::transaction::objects::TransactionExecutionInfo;
use cairo_vm::Felt252;
Expand All @@ -12,7 +12,7 @@ use starknet::core::types::BlockWithTxs;
use starknet_api::core::{ContractAddress, PatriciaKey};
use starknet_api::state::StorageKey;
use starknet_api::{contract_address, felt, patricia_key};
use starknet_os::config::DEFAULT_STORAGE_TREE_HEIGHT;
use starknet_os::config::{DEFAULT_STORAGE_TREE_HEIGHT, STORED_BLOCK_HASH_BUFFER};
use starknet_os::starkware_utils::commitment_tree::base_types::Height;
use starknet_types_core::felt::Felt;

Expand Down Expand Up @@ -111,6 +111,36 @@ fn verify_storage_proof(contract_data: &ContractData, keys: &[Felt]) -> Vec<Felt
additional_keys
}

/// Inserts additional keys for retrieving storage proof from the block hash contract (address 0x1).
/// Certain contracts necessitate extra nodes from the contract 0x1. However, since Blockifier does not provide this information,
/// it is necessary to add some extra keys to ensure the inclusion of the required nodes.
/// This approach serves as a workaround. The ideal solutions would be to either retrieve the full tree or obtain information about the necessary nodes.
/// The first approach would introduce significant overhead for most blocks, and the second solution is currently not feasible at the moment.
fn insert_extra_storage_reads_keys(
old_block_number: Felt252,
keys: &mut HashMap<ContractAddress, HashSet<StorageKey>>,
) {
// A list of the contracts that accessed to the storage from 0x1 using `get_block_hash_syscall`
let special_addresses: Vec<ContractAddress> = vec![
contract_address!("0x01246c3031c5d0d1cf60a9370aac03a4717538f659e4a2bfb0f692e970e0c4b5"),
contract_address!("0x00656ca4889a405ec5222e4b0997e5a043902a98cb1f85a039f76f50c000479d"),
contract_address!("0x022207b425a6c0239bbf5d58fbf0272fbb059ee4bb89f48255321d6e7c1606ef"),
// Ekubo:core contract address. Source code is not available but `key_not_in_preimage` error is triggered every time it's called
contract_address!("0x5dd3d2f4429af886cd1a3b08289dbcea99a294197e9eb43b0e0325b4b"),
];

if special_addresses.iter().any(|address| keys.contains_key(address)) {
let extra_storage_reads = 200 * STORED_BLOCK_HASH_BUFFER;
if old_block_number >= Felt252::from(extra_storage_reads) {
for i in 1..=extra_storage_reads {
keys.entry(contract_address!("0x1"))
.or_default()
.insert((old_block_number - i).try_into().expect("Felt to StorageKey conversion failed"));
}
}
}
}

pub(crate) async fn get_storage_proofs(
client: &RpcClient,
block_number: u64,
Expand All @@ -121,6 +151,8 @@ pub(crate) async fn get_storage_proofs(
let mut keys = get_all_accessed_keys(tx_execution_infos);
// We need to fetch the storage proof for the block hash contract
keys.entry(contract_address!("0x1")).or_default().insert(old_block_number.try_into().unwrap());
// Include extra keys for contracts that trigger get_block_hash_syscall
insert_extra_storage_reads_keys(old_block_number, &mut keys);
keys
};

Expand All @@ -132,6 +164,7 @@ pub(crate) async fn get_storage_proofs(
let contract_address_felt = *contract_address.key();
let storage_proof =
get_storage_proof_for_contract(client, contract_address, storage_keys.into_iter(), block_number).await?;

storage_proofs.insert(contract_address_felt, storage_proof);
}

Expand Down
15 changes: 15 additions & 0 deletions crates/bin/prove_block/tests/prove_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ const DEFAULT_COMPILED_OS: &[u8] = include_bytes!("../../../../build/os_latest.j
#[case::peekable_peek_is_none(174156)]
#[case::no_more_storage_reads_available(161884)]
#[case::no_more_storage_reads_available(174027)]
#[case::memory_addresses_must_be_relocatable(202083)]
#[case::memory_invalid_signature(216914)]
#[case::diff_assert_values(218624)]
#[case::could_nt_compute_operand_op1(204337)]
// The following ten tests were added due key not found in preimage (verify_game contract function related)
#[case::key_not_found_in_preimage_0(237025)]
#[case::key_not_found_in_preimage_1(237030)]
#[case::key_not_found_in_preimage_2(237037)]
#[case::key_not_found_in_preimage_3(237042)]
#[case::key_not_found_in_preimage_4(237044)]
#[case::key_not_found_in_preimage_5(237053)]
#[case::key_not_found_in_preimage_6(237083)]
#[case::key_not_found_in_preimage_7(237086)]
#[case::key_not_found_in_preimage_8(235385)]
#[case::key_not_found_in_preimage_9(235620)]
#[ignore = "Requires a running Pathfinder node"]
#[tokio::test(flavor = "multi_thread")]
async fn test_prove_selected_blocks(#[case] block_number: u64) {
Expand Down
2 changes: 0 additions & 2 deletions crates/starknet-os/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use starknet_api::core::{ChainId, ContractAddress, PatriciaKey};
use starknet_api::{contract_address, felt, patricia_key};

use crate::error::SnOsError;
use crate::utils::ChainIdNum;

pub const fn default_layout() -> LayoutName {
LayoutName::all_cairo
Expand Down Expand Up @@ -45,7 +44,6 @@ pub const GLOBAL_STATE_VERSION: &[u8] = "STARKNET_STATE_V0".as_bytes();
#[serde_as]
#[derive(Debug, Serialize, Clone, Deserialize, PartialEq)]
pub struct StarknetOsConfig {
#[serde_as(as = "ChainIdNum")]
pub chain_id: ChainId,
pub fee_token_address: ContractAddress,
pub deprecated_fee_token_address: ContractAddress,
Expand Down
7 changes: 4 additions & 3 deletions crates/starknet-os/src/hints/execute_transactions.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashMap;

use cairo_vm::hint_processor::builtin_hint_processor::hint_utils::{
get_integer_from_var_name, get_ptr_from_var_name, get_relocatable_from_var_name, insert_value_from_var_name,
get_integer_from_var_name, get_ptr_from_var_name, insert_value_from_var_name,
};
use cairo_vm::hint_processor::builtin_hint_processor::sha256_utils::sha256_finalize;
use cairo_vm::hint_processor::hint_processor_definition::HintReference;
Expand Down Expand Up @@ -38,8 +38,9 @@ where
{
let execution_helper: ExecutionHelperWrapper<PCS> = exec_scopes.get(vars::scopes::EXECUTION_HELPER)?;
let execution_context_ptr =
get_relocatable_from_var_name(vars::ids::VALIDATE_DECLARE_EXECUTION_CONTEXT, vm, ids_data, ap_tracking)?;
let deprecated_tx_info_ptr = (execution_context_ptr + ExecutionContext::deprecated_tx_info_offset())?;
get_ptr_from_var_name(vars::ids::VALIDATE_DECLARE_EXECUTION_CONTEXT, vm, ids_data, ap_tracking)?;
let deprecated_tx_info_ptr =
vm.get_relocatable((execution_context_ptr + ExecutionContext::deprecated_tx_info_offset())?)?;

execution_helper.start_tx(Some(deprecated_tx_info_ptr)).await;

Expand Down
42 changes: 42 additions & 0 deletions crates/starknet-os/src/hints/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,48 @@ mod tests {
// * hash_ptr.result
}

#[rstest]
fn test_prepare_load_bottom(_os_input: StarknetOsInput) {
let mut vm = VirtualMachine::new(false);
vm.add_memory_segment();
vm.add_memory_segment();
vm.add_memory_segment();
vm.set_fp(2);

let ap_tracking = ApTracking::new();
let constants = HashMap::new();

let ids_data = HashMap::from([
(vars::ids::EDGE.to_string(), HintReference::new_simple(-2)),
(vars::ids::HASH_PTR.to_string(), HintReference::new_simple(-2)),
]);

vm.insert_value((Relocatable::from((1, 0)) + NodeEdge::bottom_offset()).unwrap(), 1_usize).unwrap();

let hash_ptr = Relocatable::from((2, 0));
insert_value_from_var_name(vars::ids::HASH_PTR, hash_ptr, &mut vm, &ids_data, &ap_tracking).unwrap();

let mut exec_scopes: ExecutionScopes = Default::default();

let mut preimage: HashMap<Felt252, Vec<Felt252>> = Default::default();
preimage.insert(1_usize.into(), vec![2_usize.into(), 3_usize.into()]);
exec_scopes.insert_value(vars::scopes::PREIMAGE, preimage);
exec_scopes.insert_value(vars::scopes::PATRICIA_TREE_MODE, PatriciaTreeMode::State);
exec_scopes
.insert_value::<Option<PatriciaSkipValidationRunner>>(vars::scopes::PATRICIA_SKIP_VALIDATION_RUNNER, None);

load_bottom(&mut vm, &mut exec_scopes, &ids_data, &ap_tracking, &constants).unwrap();

let x = vm.get_integer((hash_ptr + 0_usize).unwrap()).unwrap();
let y = vm.get_integer((hash_ptr + 1_usize).unwrap()).unwrap();

let expected_x: Felt252 = 2_usize.into();
let expected_y: Felt252 = 3_usize.into();

assert_eq!(x.to_raw(), expected_x.to_raw());
assert_eq!(y.to_raw(), expected_y.to_raw());
}

#[test]
pub fn test_write_split_result() {
let mut vm = VirtualMachine::new(false);
Expand Down
8 changes: 6 additions & 2 deletions crates/starknet-os/src/storage/composite_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,22 @@ mod tests {

let key1 = vec![0, 1, 1];
let key2 = vec![0, 2, 2];
let key3 = vec![0, 3, 3];
let value1 = "hello".as_bytes().to_vec();
let value2 = "goodbye".as_bytes().to_vec();
let value3 = "yes".as_bytes().to_vec();

s1.set_value(key1.clone(), value1.clone()).await.unwrap();
s2.set_value(key2.clone(), value2.clone()).await.unwrap();

let storage = CompositeStorage::new(s1, s2);
let mut storage = CompositeStorage::new(s1, s2);

storage.set_value(key3.clone(), value3.clone()).await.unwrap();
assert_eq!(storage.get_value(&key1).await.unwrap(), Some(value1));
assert_eq!(storage.get_value(&key2).await.unwrap(), Some(value2));
assert_eq!(storage.get_value(&key3).await.unwrap(), Some(value3));

let missing_key = vec![0, 3, 3];
let missing_key = vec![0, 4, 4];
assert_eq!(storage.get_value(&missing_key).await.unwrap(), None);
}
}
16 changes: 0 additions & 16 deletions crates/starknet-os/src/storage/storage_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,19 +116,3 @@ pub async fn build_starknet_storage_async<S: Storage + Send + Sync, H: HashFunct

Ok((storage_by_address, initial_state, final_state))
}

/// Convert a starknet_api deprecated ContractClass to a cairo-vm ContractClass (v0 only).
/// Note that this makes a serialize -> deserialize pass, so it is not cheap!
pub fn deprecated_contract_class_api2vm(
api_class: &starknet_api::deprecated_contract_class::ContractClass,
) -> serde_json::Result<blockifier::execution::contract_class::ContractClass> {
let serialized = serde_json::to_string(&api_class)?;

let vm_class_v0_inner: blockifier::execution::contract_class::ContractClassV0Inner =
serde_json::from_str(serialized.as_str())?;

let vm_class_v0 = blockifier::execution::contract_class::ContractClassV0(std::sync::Arc::new(vm_class_v0_inner));
let vm_class = blockifier::execution::contract_class::ContractClass::V0(vm_class_v0);

Ok(vm_class)
}
96 changes: 63 additions & 33 deletions crates/starknet-os/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ use cairo_vm::{any_box, Felt252};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Number;
use serde_with::{DeserializeAs, SerializeAs};
use starknet_api::core::ChainId;
use starknet_os_types::chain_id::chain_id_to_felt;
use tokio::task;

pub(crate) struct Felt252Str;
Expand Down Expand Up @@ -80,30 +78,6 @@ impl SerializeAs<Felt252> for Felt252HexNoPrefix {
}
}

pub(crate) struct ChainIdNum;

impl<'de> DeserializeAs<'de, ChainId> for ChainIdNum {
fn deserialize_as<D>(deserializer: D) -> Result<ChainId, D::Error>
where
D: Deserializer<'de>,
{
let bytes = u128::deserialize(deserializer)?.to_be_bytes();
let chain_id_str = String::from_utf8_lossy(&bytes);

Ok(ChainId::from(chain_id_str.into_owned()))
}
}

impl SerializeAs<ChainId> for ChainIdNum {
fn serialize_as<S>(value: &ChainId, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let chain_id_felt = chain_id_to_felt(value);
chain_id_felt.serialize(serializer)
}
}

/// Retrieves a constant from the `constants` hashmap or returns an error.
///
/// We should not use `get_constant_from_var_name` if possible as it performs an O(N)
Expand Down Expand Up @@ -170,15 +144,71 @@ mod tests {
use super::*;

#[serde_as]
#[derive(Serialize)]
struct ChainIdOnly {
#[serde_as(as = "ChainIdNum")]
chain_id: ChainId,
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct FeltStrOnly {
#[serde_as(as = "Felt252Str")]
felt: Felt252,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct FeltNumOnly {
#[serde_as(as = "Felt252Num")]
felt: Felt252,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct FeltHexOnly {
#[serde_as(as = "Felt252HexNoPrefix")]
felt: Felt252,
}

#[test]
fn test_utils_felt_252_str_ok() {
let expected = "{\"felt\":\"0x0\"}".to_owned();
let f = FeltStrOnly { felt: Felt252::ZERO };
let result = serde_json::to_string(&f).unwrap();
assert_eq!(expected, result);
}

#[test]
fn test_utils_felt_252_str_deser_ok() {
let expected = FeltStrOnly { felt: Felt252::ONE };
let json = r#"{ "felt": "0x1" }"#;
let result: FeltStrOnly = serde_json::from_str(json).unwrap();
assert_eq!(expected, result);
}

#[test]
fn test_utils_felt_252_num_ok() {
let expected = "{\"felt\":0}".to_owned();
let f = FeltNumOnly { felt: Felt252::ZERO };
let result = serde_json::to_string(&f).unwrap();
assert_eq!(expected, result);
}

#[test]
fn test_utils_felt_252_num_deser_ok() {
let expected = FeltNumOnly { felt: Felt252::ONE };
let json = r#"{ "felt": 1 }"#;
let result: FeltNumOnly = serde_json::from_str(json).unwrap();
assert_eq!(expected, result);
}

#[test]
fn test_utils_felt_252_hex_ok() {
let expected = "{\"felt\":\"00\"}".to_owned();
let f = FeltHexOnly { felt: Felt252::ZERO };
let result = serde_json::to_string(&f).unwrap();
assert_eq!(expected, result);
}

#[test]
fn chain_id_num_ok() {
let c = ChainIdOnly { chain_id: ChainId::Sepolia };
serde_json::to_string(&c).unwrap();
fn test_utils_felt_252_hex_deser_ok() {
let expected = FeltHexOnly { felt: Felt252::ONE };
let json = r#"{ "felt": "1" }"#;
let result: FeltHexOnly = serde_json::from_str(json).unwrap();
assert_eq!(expected, result);
}
}
7 changes: 0 additions & 7 deletions scripts/check-orphans.sh

This file was deleted.

Loading

0 comments on commit e4b5e3e

Please sign in to comment.