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
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
"proof_facts": [
"0x5649525455414c5f534e4f53",
"0x130206a40921880628605041292e995870334451179c63090221210893986a2",
"0x3",
"0x2",
"0x7a0",
"0x2fa80",
"0x1"
],
"type": "INVOKE_FUNCTION"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@
"proof_facts": [
"0x5649525455414c5f534e4f53",
"0x130206a40921880628605041292e995870334451179c63090221210893986a2",
"0x3",
"0x2",
"0x7a0",
"0x2fa80",
"0x1"
],
"type":"INVOKE_FUNCTION"
Expand Down
9 changes: 8 additions & 1 deletion crates/apollo_gateway/src/gateway_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use apollo_network_types::network_types::BroadcastedMessageMetadata;
use apollo_test_utils::{get_rng, GetTestInstance};
use blockifier::blockifier::config::ContractClassManagerConfig;
use blockifier::context::ChainInfo;
use blockifier::test_utils::generate_block_hash_storage_updates;
use blockifier::test_utils::initial_test_state::fund_account;
use blockifier_test_utils::cairo_versions::{CairoVersion, RunnableCairo1};
use blockifier_test_utils::calldata::create_trivial_calldata;
Expand Down Expand Up @@ -283,14 +284,20 @@ async fn setup_mock_state(

setup_transaction_converter_mock(&mut mock_dependencies.mock_transaction_converter, tx_args);

// Setup state: fund account and store proof block hash if needed.
let state_reader =
&mut mock_dependencies.state_reader_factory.state_reader.blockifier_state_reader;
let address = expected_internal_tx.contract_address();
fund_account(
&mock_dependencies.config.chain_info,
address,
VALID_ACCOUNT_BALANCE,
&mut mock_dependencies.state_reader_factory.state_reader.blockifier_state_reader,
state_reader,
);

let block_hash_state_maps = generate_block_hash_storage_updates();
state_reader.storage_view.extend(block_hash_state_maps.storage);

let mempool_add_tx_args = AddTransactionArgs {
tx: expected_internal_tx.clone(),
account_state: AccountState { address, nonce: *input_tx.nonce() },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
"proof_facts": [
"0x5649525455414c5f534e4f53",
"0x130206a40921880628605041292e995870334451179c63090221210893986a2",
"0x3",
"0x2",
"0x7a0",
"0x2fa80",
"0x1"
],
"proof": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
"proof_facts": [
"0x5649525455414c5f534e4f53",
"0x130206a40921880628605041292e995870334451179c63090221210893986a2",
"0x3",
"0x2",
"0x7a0",
"0x2fa80",
"0x1"
]
}
26 changes: 26 additions & 0 deletions crates/blockifier/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ use starknet_api::execution_resources::{GasAmount, GasVector};
use starknet_api::hash::StarkHash;
use starknet_api::state::StorageKey;
use starknet_api::test_utils::{
BLOCK_HASH_HISTORY_RANGE,
CURRENT_BLOCK_NUMBER,
DEFAULT_L1_DATA_GAS_MAX_AMOUNT,
DEFAULT_L1_GAS_AMOUNT,
DEFAULT_L2_GAS_MAX_AMOUNT,
Expand All @@ -41,6 +43,7 @@ use starknet_api::transaction::fields::{
Fee,
GasVectorComputationMode,
};
use starknet_api::versioned_constants_logic::VersionedConstantsTrait;
use starknet_api::{contract_address, felt};
use starknet_types_core::felt::Felt;
use strum::EnumCount;
Expand All @@ -57,6 +60,7 @@ use crate::execution::syscalls::vm_syscall_utils::{
SyscallUsageMap,
};
use crate::fee::resources::{StarknetResources, StateResources};
use crate::state::cached_state::StateMaps;
use crate::utils::{const_max, u64_from_usize};
// Class hashes.
// TODO(Adi, 15/01/2023): Remove and compute the class hash corresponding to the ERC20 contract in
Expand Down Expand Up @@ -416,3 +420,25 @@ pub fn maybe_dummy_block_hash_and_number(block_number: BlockNumber) -> Option<Bl
hash: BlockHash(StarkHash::ONE),
})
}

/// Returns the contract address for the block hash contract used in tests.
pub fn block_hash_contract_address() -> ContractAddress {
VersionedConstants::latest_constants()
.os_constants
.os_contract_addresses
.block_hash_contract_address()
}

/// Generates deterministic block hash storage updates for historical blocks.
/// Populates a range of blocks with deterministic hash values (block_num * 100).
pub fn generate_block_hash_storage_updates() -> StateMaps {
let block_hash_history_start = CURRENT_BLOCK_NUMBER - BLOCK_HASH_HISTORY_RANGE;
let block_hash_history_end = CURRENT_BLOCK_NUMBER - constants::STORED_BLOCK_HASH_BUFFER - 1;
let block_hash_contract = block_hash_contract_address();
let storage = (block_hash_history_start..=block_hash_history_end)
.map(|block_num| {
((block_hash_contract, StorageKey::from(block_num)), Felt::from(block_num * 100))
})
.collect();
StateMaps { storage, ..Default::default() }
}
1 change: 1 addition & 0 deletions crates/blockifier/src/test_utils/initial_test_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub fn setup_test_state(
}
}

// TODO(Meshi): Add block hash storage updates.
pub fn test_state(
chain_info: &ChainInfo,
initial_balances: Fee,
Expand Down
93 changes: 73 additions & 20 deletions crates/blockifier/src/transaction/account_transaction.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use std::sync::Arc;

use starknet_api::abi::abi_utils::selector_from_name;
use starknet_api::block::GasPriceVector;
use starknet_api::block::{BlockNumber, GasPriceVector};
use starknet_api::calldata;
use starknet_api::contract_class::EntryPointType;
use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce};
use starknet_api::data_availability::DataAvailabilityMode;
use starknet_api::executable_transaction::{AccountTransaction as Transaction, TransactionType};
use starknet_api::execution_resources::GasAmount;
use starknet_api::state::StorageKey;
use starknet_api::transaction::fields::Resource::{L1DataGas, L1Gas, L2Gas};
use starknet_api::transaction::fields::{
AccountDeploymentData,
Expand All @@ -24,6 +25,7 @@ use starknet_api::transaction::{constants, TransactionHash, TransactionVersion};
use starknet_types_core::felt::Felt;

use super::errors::ResourceBoundsError;
use crate::abi::constants::STORED_BLOCK_HASH_BUFFER;
use crate::blockifier_versioned_constants::OsConstants;
use crate::context::{BlockContext, GasCounter, TransactionContext};
use crate::execution::call_info::CallInfo;
Expand Down Expand Up @@ -250,26 +252,73 @@ impl AccountTransaction {
fn validate_proof_facts(
&self,
os_constants: &OsConstants,
current_block_number: BlockNumber,
state: &mut dyn State,
) -> TransactionPreValidationResult<()> {
if let Transaction::Invoke(tx) = &self.tx {
if tx.tx.version() == TransactionVersion::THREE {
let proof_facts_variant = ProofFactsVariant::try_from(&tx.tx.proof_facts())
.map_err(|e| TransactionPreValidationError::InvalidProofFacts(e.to_string()))?;
match proof_facts_variant {
ProofFactsVariant::Empty => {}
ProofFactsVariant::Snos(snos_proof_facts) => {
// Validates the proof facts program hash.
let allowed = &os_constants.allowed_virtual_os_program_hashes;
if !allowed.contains(&snos_proof_facts.program_hash) {
return Err(TransactionPreValidationError::InvalidProofFacts(format!(
"Virtual OS program hash {} is not allowed",
snos_proof_facts.program_hash
)));
}
}
}
}
// Only Invoke V3 transactions can carry proof facts.
let Transaction::Invoke(invoke_tx) = &self.tx else {
return Ok(());
};
if invoke_tx.version() < TransactionVersion::THREE {
return Ok(());
}

// Parse proof facts.
let proof_facts = invoke_tx.proof_facts();
let snos_proof_facts = match ProofFactsVariant::try_from(&proof_facts)
.map_err(|e| TransactionPreValidationError::InvalidProofFacts(e.to_string()))?
{
ProofFactsVariant::Empty => return Ok(()),
ProofFactsVariant::Snos(snos_proof_facts) => snos_proof_facts,
};

// Proof block must be old enough to have a stored block hash.
// Stored block hashes are guaranteed only up to: current - STORED_BLOCK_HASH_BUFFER.
let max_allowed =
current_block_number.0.checked_sub(STORED_BLOCK_HASH_BUFFER).ok_or_else(|| {
TransactionPreValidationError::InvalidProofFacts(format!(
"The current block number {current_block_number} is too recent to have a \
stored block hash."
))
})?;

let proof_block_number = snos_proof_facts.block_number.0;
if proof_block_number >= max_allowed {
return Err(TransactionPreValidationError::InvalidProofFacts(format!(
"The proof block number {proof_block_number} is too recent. The maximum allowed \
block number is {max_allowed}."
)));
}

// Compare the proof's block hash with the stored block hash.
let block_hash_contract_address =
os_constants.os_contract_addresses.block_hash_contract_address();

let stored_block_hash = state
.get_storage_at(block_hash_contract_address, StorageKey::from(proof_block_number))?;

let proof_block_hash = snos_proof_facts.block_hash.0;
if proof_block_hash == Felt::ZERO {
return Err(TransactionPreValidationError::InvalidProofFacts(format!(
"Stored block hash is zero for block {proof_block_number}."
)));
}
if stored_block_hash != proof_block_hash {
return Err(TransactionPreValidationError::InvalidProofFacts(format!(
"Block hash mismatch for block {proof_block_number}. Proof block hash: \
{proof_block_hash}, stored block hash: {stored_block_hash}."
)));
}

// Validates the proof facts program hash.
let allowed = &os_constants.allowed_virtual_os_program_hashes;
if !allowed.contains(&snos_proof_facts.program_hash) {
return Err(TransactionPreValidationError::InvalidProofFacts(format!(
"Virtual OS program hash {} is not allowed",
snos_proof_facts.program_hash
)));
}

Ok(())
}

Expand All @@ -289,7 +338,11 @@ impl AccountTransaction {
verify_can_pay_committed_bounds(state, tx_context).map_err(Box::new)?;
}

self.validate_proof_facts(&tx_context.block_context.versioned_constants.os_constants)?;
self.validate_proof_facts(
&tx_context.block_context.versioned_constants.os_constants,
tx_context.block_context.block_info.block_number,
state,
)?;

Ok(())
}
Expand Down
14 changes: 11 additions & 3 deletions crates/starknet_api/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ pub const TEST_ERC20_CONTRACT_ADDRESS2: &str = "0x1002";
pub const CURRENT_BLOCK_NUMBER: u64 = 2001;
pub const CURRENT_BLOCK_NUMBER_FOR_VALIDATE: u64 = 2000;

// Range of historical blocks to populate in the block hash contract.
pub const BLOCK_HASH_HISTORY_RANGE: u64 = 51;

// The block timestamp of the BlockContext being used for testing.
pub const CURRENT_BLOCK_TIMESTAMP: u64 = 1072023;
pub const CURRENT_BLOCK_TIMESTAMP_FOR_VALIDATE: u64 = 1069200;
Expand Down Expand Up @@ -378,11 +381,16 @@ impl ProofFacts {
///
/// See [`crate::transaction::fields::ProofFacts`].
pub fn snos_proof_facts_for_testing() -> Self {
let block_hash_history_start = CURRENT_BLOCK_NUMBER - BLOCK_HASH_HISTORY_RANGE;
let block_number = felt!(block_hash_history_start + 2);
let block_hash = block_number * felt!(100_u64);
assert!(
block_number < felt!(CURRENT_BLOCK_NUMBER)
&& block_number >= felt!(block_hash_history_start),
"Block number is out of range"
);
// TODO(AvivG): Change to valid values when available.
let block_number = felt!("0x3");
let block_hash = felt!("0x2");
let config_hash = felt!("0x1");

proof_facts![
felt!(VIRTUAL_SNOS),
VIRTUAL_OS_PROGRAM_HASH,
Expand Down
15 changes: 13 additions & 2 deletions crates/starknet_os_flow_tests/src/initial_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::{BTreeMap, HashMap, HashSet};

use blockifier::context::BlockContext;
use blockifier::state::state_api::UpdatableState;
use blockifier::test_utils::generate_block_hash_storage_updates;
use blockifier::transaction::transaction_execution::Transaction;
use blockifier_test_utils::cairo_versions::{CairoVersion, RunnableCairo1};
use blockifier_test_utils::calldata::create_calldata;
Expand Down Expand Up @@ -164,17 +165,27 @@ pub(crate) async fn create_default_initial_state_data<S: FlowTestState, const N:
// Make sure none of them is reverted.
assert!(execution_outputs.iter().all(|output| output.0.revert_error.is_none()));
// Update the state reader with the state diff.
let state_diff = final_state.to_state_diff().unwrap().state_maps;
let mut state_diff = final_state.to_state_diff().unwrap().state_maps;
// Sanity check to verify the STRK_FEE_TOKEN_ADDRESS constant.
assert_eq!(
state_diff.class_hashes[&STRK_FEE_TOKEN_ADDRESS],
FeatureContract::ERC20(CairoVersion::Cairo1(RunnableCairo1::Casm))
.get_sierra()
.calculate_class_hash()
);
// Add historical block hashes.
let block_hash_state_diff = generate_block_hash_storage_updates();
state_diff.extend(&block_hash_state_diff);

final_state.state.apply_writes(&state_diff, &final_state.class_hash_to_class.borrow());

// Commit the state diff.
// Add historical block hashes.
let block_hash_state_maps = generate_block_hash_storage_updates();
final_state
.state
.apply_writes(&block_hash_state_maps, &final_state.class_hash_to_class.borrow());

// Commits the state diff with block hash mappings.
let committer_state_diff = create_committer_state_diff(state_diff);
let (commitment_output, commitment_storage) =
commit_initial_state_diff(committer_state_diff).await;
Expand Down
5 changes: 4 additions & 1 deletion crates/starknet_os_flow_tests/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,7 @@ async fn test_new_class_execution_info(#[values(true, false)] use_kzg_da: bool)
TestBuilderConfig { use_kzg_da, ..Default::default() },
)
.await;

let chain_id = &test_builder.chain_id();
let current_block_number = test_builder.first_block_number();

Expand Down Expand Up @@ -1105,6 +1106,7 @@ async fn test_new_class_execution_info(#[values(true, false)] use_kzg_da: bool)
// Test calling test_get_execution_info.
let test_call_contract_selector_name = "test_call_contract";
let test_execution_info_selector = selector_from_name("test_get_execution_info");
let proof_facts = ProofFacts::snos_proof_facts_for_testing();
let only_query = false;
let expected_execution_info = ExpectedExecutionInfo::new(
only_query,
Expand All @@ -1118,7 +1120,7 @@ async fn test_new_class_execution_info(#[values(true, false)] use_kzg_da: bool)
contract_address!(TEST_SEQUENCER_ADDRESS),
*NON_TRIVIAL_RESOURCE_BOUNDS,
test_builder.get_nonce(*FUNDED_ACCOUNT_ADDRESS),
ProofFacts::default(),
proof_facts.clone(),
)
.to_syscall_result();
let invoke_tx_args = invoke_tx_args! {
Expand All @@ -1131,6 +1133,7 @@ async fn test_new_class_execution_info(#[values(true, false)] use_kzg_da: bool)
expected_execution_info.len().into()
].into_iter().chain(expected_execution_info.into_iter()).collect::<Vec<_>>()
),
proof_facts,
};
// Put the tx hash in the signature.
let mut invoke_tx = test_builder.create_funded_account_invoke(invoke_tx_args);
Expand Down
Loading