Skip to content

start adding a get_random syscall #7

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

Draft
wants to merge 8 commits into
base: cairo-2.4
Choose a base branch
from
Draft
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
176 changes: 152 additions & 24 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@ pretty_assertions = "1.2.1"
rstest = "0.17.0"
serde = "1.0.184"
serde_json = "1.0.81"
sha3 = "0.10.6"
sha3 = "0.10.8"
starknet-crypto = "0.5.1"
starknet_api = "0.6.0-rc3"
strum = "0.24.1"
strum_macros = "0.24.3"
tempfile = "3.7.0"
test-case = "2.2.2"
thiserror = "1.0.37"
ecvrf = "0.4.3"

[patch.crates-io]
cairo-felt = { git = "https://github.com/dojoengine/cairo-rs.git", rev = "262b7eb4b11ab165a2a936a5f914e78aa732d4a2" }
Expand Down
1 change: 1 addition & 0 deletions crates/blockifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ strum_macros.workspace = true
thiserror.workspace = true
phf.workspace = true
ctor.workspace = true
ecvrf.workspace = true

[dev-dependencies]
assert_matches.workspace = true
Expand Down
1 change: 1 addition & 0 deletions crates/blockifier/src/abi/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub const DEPLOY_GAS_COST: u64 = 200 * STEP_GAS_COST + ENTRY_POINT_GAS_COST;
pub const EMIT_EVENT_GAS_COST: u64 = 10 * STEP_GAS_COST;
pub const GET_BLOCK_HASH_GAS_COST: u64 = 50 * STEP_GAS_COST;
pub const GET_EXECUTION_INFO_GAS_COST: u64 = 10 * STEP_GAS_COST;
pub const GET_RANDOM_GAS_COST: u64 = 10 * STEP_GAS_COST;
pub const KECCAK_GAS_COST: u64 = 0;
pub const KECCAK_ROUND_COST_GAS_COST: u64 = 180000;
pub const LIBRARY_CALL_GAS_COST: u64 = CALL_CONTRACT_GAS_COST;
Expand Down
5 changes: 5 additions & 0 deletions crates/blockifier/src/block_context.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::HashMap;
use std::sync::Arc;

use ecvrf::{VrfPk, VrfSk};
use starknet_api::block::{BlockNumber, BlockTimestamp};
use starknet_api::core::{ChainId, ContractAddress};

Expand All @@ -22,6 +23,10 @@ pub struct BlockContext {
pub invoke_tx_max_n_steps: u32,
pub validate_max_n_steps: u32,
pub max_recursion_depth: usize,

// ECVRF
pub ecvrf_private_key: VrfSk,
pub ecvrf_public_key: VrfPk,
}

impl BlockContext {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ fn test_call_contract() {
calldata: calldata.clone(),
..trivial_external_entry_point()
};
let call_info = entry_point_call.clone().execute_directly(&mut state).unwrap();
let call_info = entry_point_call.execute_directly(&mut state).unwrap();

let expected_execution = CallExecution { retdata: retdata![value], ..Default::default() };
let expected_inner_call_info = CallInfo {
Expand Down
6 changes: 5 additions & 1 deletion crates/blockifier/src/execution/entry_point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ pub struct EntryPointExecutionContext {

// The execution mode affects the behavior of the hint processor.
pub execution_mode: ExecutionMode,

/// Used to modify get_random seed.
pub n_requested_ecvrf: usize,
}

impl EntryPointExecutionContext {
Expand All @@ -174,6 +177,7 @@ impl EntryPointExecutionContext {
max_recursion_depth: block_context.max_recursion_depth,
block_context: block_context.clone(),
execution_mode: mode,
n_requested_ecvrf: 0,
})
}

Expand Down Expand Up @@ -384,7 +388,7 @@ struct RecursionDepthGuard {

impl RecursionDepthGuard {
fn new(current_depth: Arc<RefCell<usize>>, max_depth: usize) -> Self {
Self { current_depth: current_depth.clone(), max_depth }
Self { current_depth, max_depth }
}

// Tries to increment the current recursion depth and returns an error if the maximum depth
Expand Down
3 changes: 2 additions & 1 deletion crates/blockifier/src/execution/hint_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub static SYSCALL_HINTS: phf::Set<&'static str> = phf_set! {
"syscall_handler.get_block_timestamp(segments=segments, syscall_ptr=ids.syscall_ptr)",
"syscall_handler.get_caller_address(segments=segments, syscall_ptr=ids.syscall_ptr)",
"syscall_handler.get_contract_address(segments=segments, syscall_ptr=ids.syscall_ptr)",
"syscall_handler.get_random(segments=segments, syscall_ptr=ids.syscall_ptr)",
"syscall_handler.get_sequencer_address(segments=segments, syscall_ptr=ids.syscall_ptr)",
"syscall_handler.get_tx_info(segments=segments, syscall_ptr=ids.syscall_ptr)",
"syscall_handler.get_tx_signature(segments=segments, syscall_ptr=ids.syscall_ptr)",
Expand All @@ -24,7 +25,7 @@ pub static SYSCALL_HINTS: phf::Set<&'static str> = phf_set! {
pub const NORMALIZE_ADDRESS_SET_IS_250_HINT: &str = "ids.is_250 = 1 if ids.addr < 2**250 else 0";

#[rustfmt::skip]
pub const NORMALIZE_ADDRESS_SET_IS_SMALL_HINT: &str =
pub const NORMALIZE_ADDRESS_SET_IS_SMALL_HINT: &str =
r"# Verify the assumptions on the relationship between 2**250, ADDR_BOUND and PRIME.
ADDR_BOUND = ids.ADDR_BOUND % PRIME
assert (2**250 < ADDR_BOUND <= 2**251) and (2 * 2**250 < PRIME) and (
Expand Down
101 changes: 100 additions & 1 deletion crates/blockifier/src/execution/syscalls/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use cairo_felt::Felt252;
use cairo_vm::types::relocatable::Relocatable;
use cairo_vm::vm::vm_core::VirtualMachine;
use ecvrf::VrfPk;
use num_bigint::BigUint;
use num_traits::ToPrimitive;
use starknet_api::block::{BlockHash, BlockNumber};
use starknet_api::core::{
Expand All @@ -25,7 +27,7 @@ use crate::execution::deprecated_syscalls::DeprecatedSyscallSelector;
use crate::execution::entry_point::{CallEntryPoint, CallType, ConstructorContext};
use crate::execution::execution_utils::{
execute_deployment, felt_from_ptr, felt_to_stark_felt, stark_felt_from_ptr, stark_felt_to_felt,
write_felt, write_maybe_relocatable, write_stark_felt, ReadOnlySegment,
write_felt, write_maybe_relocatable, write_stark_felt, write_u256, ReadOnlySegment,
};
use crate::execution::syscalls::hint_processor::{INVALID_INPUT_LENGTH_ERROR, OUT_OF_GAS_ERROR};
use crate::transaction::transaction_utils::update_remaining_gas;
Expand Down Expand Up @@ -385,6 +387,7 @@ impl SyscallResponse for GetExecutionInfoResponse {
Ok(())
}
}

pub fn get_execution_info(
_request: GetExecutionInfoRequest,
vm: &mut VirtualMachine,
Expand All @@ -396,6 +399,102 @@ pub fn get_execution_info(
Ok(GetExecutionInfoResponse { execution_info_ptr })
}

// GetRandom syscall.

pub struct GetRandomRequest {
pub seed: u64,
}

impl SyscallRequest for GetRandomRequest {
fn read(vm: &VirtualMachine, ptr: &mut Relocatable) -> SyscallResult<GetRandomRequest> {
let seed = felt_from_ptr(vm, ptr)?;
let seed = seed.to_u64().ok_or_else(|| SyscallExecutionError::InvalidSyscallInput {
input: felt_to_stark_felt(&seed),
info: String::from("Unexpected seed."),
})?;

Ok(GetRandomRequest { seed })
}
}

#[derive(Debug)]
pub struct GetRandomResponse {
// the random u256
pub hash: [u8; 32],
// randomness proof
pub proof: ReadOnlySegment,
// ecvrf public key
pub pk: VrfPk,
}

impl SyscallResponse for GetRandomResponse {
fn write(self, vm: &mut VirtualMachine, ptr: &mut Relocatable) -> WriteResponseResult {
// write hash
write_u256(vm, ptr, BigUint::from_bytes_le(&self.hash))?;

// write proof
write_segment(vm, ptr, self.proof)?;

// write public key
write_u256(vm, ptr, BigUint::from_bytes_le(&self.pk.to_bytes()))?;

Ok(())
}
}

pub fn get_random(
request: GetRandomRequest,
vm: &mut VirtualMachine,
syscall_handler: &mut SyscallHintProcessor<'_>,
_remaining_gas: &mut u64,
) -> SyscallResult<GetRandomResponse> {
// get useful data from execution context
let ecvrf_private_key = syscall_handler.context.block_context.ecvrf_private_key.clone();
let ecvrf_public_key = syscall_handler.context.block_context.ecvrf_public_key.clone();
let n_requested_ecvrf = syscall_handler.context.n_requested_ecvrf;
let current_block_number = syscall_handler.context.block_context.block_number.0;
let caller_address = syscall_handler.caller_address();

// get latest accessible block hash or 0 if no block hash is accessible yet
let block_hash = if current_block_number < constants::STORED_BLOCK_HASH_BUFFER {
StarkFelt::from(0_u128)
} else {
let key = StorageKey::try_from(StarkFelt::from(
current_block_number - constants::STORED_BLOCK_HASH_BUFFER,
))?;
let block_hash_contract_address =
ContractAddress::try_from(StarkFelt::from(constants::BLOCK_HASH_CONTRACT_ADDRESS))?;
syscall_handler.state.get_storage_at(block_hash_contract_address, key)?
};

// seed construction
let mut seed: Vec<u8> = Vec::new();
seed.extend_from_slice(&n_requested_ecvrf.to_le_bytes());
seed.extend_from_slice(block_hash.bytes());
seed.extend_from_slice(&request.seed.to_le_bytes());
seed.extend_from_slice(caller_address.0.key().bytes());

// compute ecvrf hash and proof
let (hash, proof) = ecvrf::prove(seed.as_slice(), &ecvrf_private_key);

// create proof felt array
let binding = proof.to_bytes();
let proof_chunks: Vec<_> = binding.chunks_exact(24).collect();
let raw_proof = [
StarkFelt::new(proof_chunks[0].try_into().unwrap()).unwrap(),
StarkFelt::new(proof_chunks[1].try_into().unwrap()).unwrap(),
StarkFelt::new(proof_chunks[2].try_into().unwrap()).unwrap(),
StarkFelt::new(proof_chunks[3].try_into().unwrap()).unwrap(),
];

let proof_segment = create_retdata_segment(vm, syscall_handler, &raw_proof)?;

// increase requested ecvrf count
syscall_handler.context.n_requested_ecvrf += 1;

Ok(GetRandomResponse { hash, proof: proof_segment, pk: ecvrf_public_key })
}

// LibraryCall syscall.

#[derive(Debug, Eq, PartialEq)]
Expand Down
2 changes: 1 addition & 1 deletion crates/blockifier/src/execution/syscalls/syscalls_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ fn test_get_execution_info(
calldata: Calldata(
[
expected_block_info.to_vec(),
expected_tx_info.clone(),
expected_tx_info,
expected_resource_bounds,
expected_call_info,
]
Expand Down
2 changes: 1 addition & 1 deletion crates/blockifier/src/fee/strk_gas_price/gas_price_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ fn test_convert_wei_to_strk() {
PoolStateAggregator::new(&[state_3, state_1.clone(), state_2.clone()])
.unwrap()
.convert_wei_to_strk(wei_amount.clone()),
(state_2.total_strk.clone() * wei_amount.clone()) / state_2.total_wei.clone()
(state_2.total_strk.clone() * wei_amount.clone()) / state_2.total_wei
);

// Convert Wei -> STRK with multiple pool states with equal weight partition.
Expand Down
8 changes: 8 additions & 0 deletions crates/blockifier/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use cairo_vm::vm::runners::builtin_runner::{
BITWISE_BUILTIN_NAME, EC_OP_BUILTIN_NAME, HASH_BUILTIN_NAME, OUTPUT_BUILTIN_NAME,
POSEIDON_BUILTIN_NAME, RANGE_CHECK_BUILTIN_NAME, SIGNATURE_BUILTIN_NAME,
};
use ecvrf::{VrfPk, VrfSk};
use num_traits::{One, Zero};
use starknet_api::block::{BlockNumber, BlockTimestamp};
use starknet_api::core::{
Expand Down Expand Up @@ -140,6 +141,8 @@ pub const CURRENT_BLOCK_TIMESTAMP: u64 = 1072023;

pub const CHAIN_ID_NAME: &str = "SN_GOERLI";

pub const ECVRF_PRIVATE_KEY: [u8; 32] = *b"80808080808080808080808080808080";

/// A simple implementation of `StateReader` using `HashMap`s as storage.
#[derive(Debug, Default)]
pub struct DictStateReader {
Expand Down Expand Up @@ -451,6 +454,9 @@ impl CallEntryPoint {

impl BlockContext {
pub fn create_for_testing() -> BlockContext {
let ecvrf_private_key = VrfSk::from_bytes(&ECVRF_PRIVATE_KEY).unwrap();
let ecvrf_public_key = VrfPk::new(&ecvrf_private_key);

BlockContext {
chain_id: ChainId(CHAIN_ID_NAME.to_string()),
block_number: BlockNumber(CURRENT_BLOCK_NUMBER),
Expand All @@ -468,6 +474,8 @@ impl BlockContext {
invoke_tx_max_n_steps: MAX_STEPS_PER_TX as u32,
validate_max_n_steps: MAX_VALIDATE_STEPS_PER_TX as u32,
max_recursion_depth: 50,
ecvrf_private_key,
ecvrf_public_key,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1281,7 +1281,7 @@ fn test_revert_on_overdraft(
fee_token_address
),
version,
resource_bounds: max_resource_bounds.clone(),
resource_bounds: max_resource_bounds,
nonce: nonce_manager.next(account_address),
},
)
Expand Down
2 changes: 1 addition & 1 deletion crates/blockifier/src/transaction/transactions_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1279,7 +1279,7 @@ fn test_only_query_flag(#[case] only_query: bool) {
[
execute_calldata,
expected_block_info.clone().to_vec(),
expected_tx_info.clone(),
expected_tx_info,
expected_call_info,
]
.concat()
Expand Down
1 change: 1 addition & 0 deletions crates/native_blockifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pyo3-log = "0.8.1"
serde_json = { workspace = true, features = ["arbitrary_precision"] }
starknet_api = { workspace = true, features = ["testing"] }
thiserror.workspace = true
ecvrf.workspace = true

[dev-dependencies]
cached.workspace = true
Expand Down
9 changes: 9 additions & 0 deletions crates/native_blockifier/src/py_block_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::sync::Arc;

use blockifier::block_context::{BlockContext, FeeTokenAddresses, GasPrices};
use blockifier::state::cached_state::GlobalContractCache;
use ecvrf::{VrfPk, VrfSk};
use pyo3::prelude::*;
use starknet_api::block::{BlockNumber, BlockTimestamp};
use starknet_api::core::{ChainId, ContractAddress};
Expand Down Expand Up @@ -225,6 +226,7 @@ pub struct PyGeneralConfig {
pub cairo_resource_fee_weights: Arc<HashMap<String, f64>>,
pub invoke_tx_max_n_steps: u32,
pub validate_max_n_steps: u32,
pub ecvrf_private_key: [u8; 32],
}

impl FromPyObject<'_> for PyGeneralConfig {
Expand All @@ -237,6 +239,7 @@ impl FromPyObject<'_> for PyGeneralConfig {
let max_strk_l1_gas_price: u128 = py_attr(general_config, "max_strk_l1_gas_price")?;
let invoke_tx_max_n_steps: u32 = py_attr(general_config, "invoke_tx_max_n_steps")?;
let validate_max_n_steps: u32 = py_attr(general_config, "validate_max_n_steps")?;
let ecvrf_private_key = general_config.getattr("ecvrf_private_key")?.extract()?;

Ok(Self {
starknet_os_config,
Expand All @@ -245,6 +248,7 @@ impl FromPyObject<'_> for PyGeneralConfig {
cairo_resource_fee_weights,
invoke_tx_max_n_steps,
validate_max_n_steps,
ecvrf_private_key,
})
}
}
Expand Down Expand Up @@ -274,6 +278,9 @@ pub fn into_block_context(
) -> NativeBlockifierResult<BlockContext> {
let starknet_os_config = general_config.starknet_os_config.clone();
let block_number = BlockNumber(block_info.block_number);
let ecvrf_private_key = VrfSk::from_bytes(&general_config.ecvrf_private_key).unwrap();
let ecvrf_public_key = VrfPk::new(&ecvrf_private_key);

let block_context = BlockContext {
chain_id: starknet_os_config.chain_id,
block_number,
Expand All @@ -295,6 +302,8 @@ pub fn into_block_context(
invoke_tx_max_n_steps: general_config.invoke_tx_max_n_steps,
validate_max_n_steps: general_config.validate_max_n_steps,
max_recursion_depth,
ecvrf_private_key,
ecvrf_public_key,
};

Ok(block_context)
Expand Down
2 changes: 2 additions & 0 deletions crates/native_blockifier/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ impl Storage {
min_size: 1 << 20, // 1MB.
max_size: config.max_size,
growth_step: 1 << 26, // 64MB.
enforce_file_exists: false,
};
let storage_config = papyrus_storage::StorageConfig {
db_config,
Expand Down Expand Up @@ -212,6 +213,7 @@ impl Storage {
min_size: 1 << 20, // 1MB
max_size: 1 << 35, // 32GB
growth_step: 1 << 26, // 64MB
enforce_file_exists: false,
};
let storage_config = papyrus_storage::StorageConfig { db_config, ..Default::default() };
let (reader, writer) = papyrus_storage::open_storage(storage_config).unwrap();
Expand Down