Skip to content

Commit

Permalink
Block generation period (#491)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikiw committed Jun 18, 2024
1 parent 3ad8145 commit 24ed52d
Show file tree
Hide file tree
Showing 16 changed files with 267 additions and 55 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions crates/starknet-devnet-core/src/starknet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use blockifier::transaction::transactions::ExecutableTransaction;
use starknet_api::block::{BlockNumber, BlockStatus, BlockTimestamp, GasPrice, GasPricePerToken};
use starknet_api::core::SequencerContractAddress;
use starknet_api::transaction::Fee;
use starknet_config::BlockGenerationOn;
use starknet_rs_core::types::{
BlockId, BlockTag, ExecutionResult, MsgFromL1, TransactionExecutionStatus,
TransactionFinalityStatus,
Expand Down Expand Up @@ -475,8 +476,8 @@ impl Starknet {

self.transactions.insert(transaction_hash, transaction_to_add);

// create new block from pending one, only if block on-demand mode is disabled
if !self.config.blocks_on_demand {
// create new block from pending one, only in block generation transaction mode
if self.config.block_generation_on == BlockGenerationOn::Transaction {
self.generate_new_block_and_state(state_diff)?;
}

Expand Down
35 changes: 33 additions & 2 deletions crates/starknet-devnet-core/src/starknet/starknet_config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::num::NonZeroU128;

use clap::Error;
use serde::{Serialize, Serializer};
use starknet_types::chain_id::ChainId;
use starknet_types::contract_class::ContractClass;
Expand Down Expand Up @@ -30,6 +31,36 @@ pub enum StateArchiveCapacity {
Full,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum BlockGenerationOn {
Transaction,
Demand,
Interval(u64),
}

impl std::str::FromStr for BlockGenerationOn {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"transaction" => Ok(BlockGenerationOn::Transaction),
"demand" => Ok(BlockGenerationOn::Demand),
value => {
let interval_value = value
.parse::<u64>()
.map_err(|_| Error::new(clap::error::ErrorKind::InvalidValue))?;

if interval_value > 0 {
Ok(BlockGenerationOn::Interval(interval_value))
} else {
Err(Error::new(clap::error::ErrorKind::InvalidValue))
}
}
}
}
}

#[derive(Debug, Clone, Default, Serialize)]
pub struct ForkConfig {
#[serde(serialize_with = "serialize_config_url")]
Expand Down Expand Up @@ -79,7 +110,7 @@ pub struct StarknetConfig {
pub chain_id: ChainId,
pub dump_on: Option<DumpOn>,
pub dump_path: Option<String>,
pub blocks_on_demand: bool,
pub block_generation_on: BlockGenerationOn,
pub lite_mode: bool,
/// on initialization, re-execute loaded txs (if any)
#[serde(skip_serializing)]
Expand Down Expand Up @@ -109,7 +140,7 @@ impl Default for StarknetConfig {
chain_id: DEVNET_DEFAULT_CHAIN_ID,
dump_on: None,
dump_path: None,
blocks_on_demand: false,
block_generation_on: BlockGenerationOn::Transaction,
lite_mode: false,
re_execute_on_init: true,
state_archive: StateArchiveCapacity::default(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,7 @@ pub async fn mint(
.await
.map_err(|err| HttpApiError::MintingError { msg: err.to_string() })?;

let block_tag =
if starknet.config.blocks_on_demand { BlockTag::Pending } else { BlockTag::Latest };

let new_balance = get_balance(&mut starknet, request.address, erc20_address, block_tag)
let new_balance = get_balance(&mut starknet, request.address, erc20_address, BlockTag::Pending)
.map_err(|err| HttpApiError::MintingError { msg: err.to_string() })?;

Ok(Json(MintTokensResponse { new_balance: new_balance.to_str_radix(10), unit, tx_hash }))
Expand Down
1 change: 1 addition & 0 deletions crates/starknet-devnet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ url = { workspace = true }

# async
tokio = { workspace = true, features = ["signal"] }
futures = { workspace = true }

# tracing
tracing = { workspace = true }
Expand Down
52 changes: 43 additions & 9 deletions crates/starknet-devnet/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use starknet_core::constants::{
use starknet_core::contract_class_choice::{AccountClassWrapper, AccountContractClassChoice};
use starknet_core::random_number_generator::generate_u32_random_number;
use starknet_core::starknet::starknet_config::{
DumpOn, ForkConfig, StarknetConfig, StateArchiveCapacity,
BlockGenerationOn, DumpOn, ForkConfig, StarknetConfig, StateArchiveCapacity,
};
use starknet_types::chain_id::ChainId;
use tracing_subscriber::EnvFilter;
Expand Down Expand Up @@ -157,10 +157,16 @@ pub(crate) struct Args {
#[arg(help = "Specify the path to dump to;")]
dump_path: Option<String>,

#[arg(long = "blocks-on-demand")]
#[arg(env = "BLOCKS_ON_DEMAND")]
#[arg(help = "Introduces block generation on demand via /create_block endpoint;")]
blocks_on_demand: bool,
#[arg(long = "block-generation-on")]
#[arg(env = "BLOCK_GENERATION_ON")]
#[arg(default_value = "transaction")]
#[arg(help = "Specify when to generate a new block. Possible values are:
- \"transaction\" - new block generated on each transaction
- \"demand\" - new block creatable solely by sending a POST request to /create_block
- <INTERVAL> - a positive integer indicating after how many seconds a new block is generated
Sending POST /create_block is also an option in modes other than \"demand\".")]
block_generation_on: BlockGenerationOn,

#[arg(long = "state-archive-capacity")]
#[arg(env = "STATE_ARCHIVE_CAPACITY")]
Expand Down Expand Up @@ -220,7 +226,7 @@ impl Args {
chain_id: self.chain_id,
dump_on: self.dump_on,
dump_path: self.dump_path.clone(),
blocks_on_demand: self.blocks_on_demand,
block_generation_on: self.block_generation_on,
lite_mode: self.lite_mode,
re_execute_on_init: true,
state_archive: self.state_archive,
Expand Down Expand Up @@ -268,7 +274,7 @@ mod tests {
use starknet_core::constants::{
CAIRO_0_ERC20_CONTRACT_PATH, CAIRO_1_ACCOUNT_CONTRACT_SIERRA_PATH,
};
use starknet_core::starknet::starknet_config::StateArchiveCapacity;
use starknet_core::starknet::starknet_config::{BlockGenerationOn, StateArchiveCapacity};
use tracing_subscriber::EnvFilter;

use super::{Args, RequestResponseLogging};
Expand Down Expand Up @@ -528,6 +534,7 @@ mod tests {
("--fork-network", "FORK_NETWORK", "http://dummy.com"),
("--fork-block", "FORK_BLOCK", "42"),
("--request-body-size-limit", "REQUEST_BODY_SIZE_LIMIT", "100"),
("--block-generation-on", "BLOCK_GENERATION_ON", "demand"),
];

let mut cli_args = vec!["--"];
Expand Down Expand Up @@ -557,8 +564,7 @@ mod tests {
#[test]
#[serial_test::serial]
fn test_boolean_param_specification_via_env_vars() {
let config_source =
[("--lite-mode", "LITE_MODE"), ("--blocks-on-demand", "BLOCKS_ON_DEMAND")];
let config_source = [("--lite-mode", "LITE_MODE")];

let mut cli_args = vec!["--"];
for (cli_param, _) in config_source {
Expand Down Expand Up @@ -586,4 +592,32 @@ mod tests {
std::env::remove_var(var_name);
}
}

#[test]
fn not_allowing_invalid_values_as_block_generation_interval() {
for interval in ["", "0", "-1", "abc"] {
match Args::try_parse_from(["--", "--block-generation-on", interval]) {
Err(_) => (),
Ok(parsed) => panic!("Should fail for {interval}; got: {parsed:?}"),
}
}
}

#[test]
fn allowing_valid_values_as_block_generation_interval() {
match Args::try_parse_from(["--", "--block-generation-on", "1"]) {
Ok(args) => assert_eq!(args.block_generation_on, BlockGenerationOn::Interval(1)),
Err(e) => panic!("Should have passed; got: {e}"),
}

match Args::try_parse_from(["--", "--block-generation-on", "demand"]) {
Ok(args) => assert_eq!(args.block_generation_on, BlockGenerationOn::Demand),
Err(e) => panic!("Should have passed; got: {e}"),
}

match Args::try_parse_from(["--", "--block-generation-on", "transaction"]) {
Ok(args) => assert_eq!(args.block_generation_on, BlockGenerationOn::Transaction),
Err(e) => panic!("Should have passed; got: {e}"),
}
}
}
62 changes: 55 additions & 7 deletions crates/starknet-devnet/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use anyhow::Ok;
use std::future::IntoFuture;
use std::result::Result::Ok;
use std::time::Duration;

use clap::Parser;
use cli::Args;
use futures::future::join_all;
use server::api::json_rpc::RPC_SPEC_VERSION;
use server::api::Api;
use server::server::serve_http_api_json_rpc;
Expand All @@ -9,7 +13,7 @@ use starknet_core::constants::{
CAIRO_1_ERC20_CONTRACT_CLASS_HASH, ETH_ERC20_CONTRACT_ADDRESS, STRK_ERC20_CONTRACT_ADDRESS,
UDC_CONTRACT_ADDRESS, UDC_CONTRACT_CLASS_HASH,
};
use starknet_core::starknet::starknet_config::{DumpOn, ForkConfig};
use starknet_core::starknet::starknet_config::{BlockGenerationOn, DumpOn, ForkConfig};
use starknet_core::starknet::Starknet;
use starknet_rs_core::types::{BlockId, BlockTag, MaybePendingBlockWithTxHashes};
use starknet_rs_providers::jsonrpc::HttpTransport;
Expand All @@ -18,6 +22,9 @@ use starknet_types::chain_id::ChainId;
use starknet_types::rpc::state::Balance;
use starknet_types::traits::ToHexString;
use tokio::net::TcpListener;
use tokio::signal::unix::{signal, SignalKind};
use tokio::task::{self};
use tokio::time::interval;
use tracing::{info, warn};
use tracing_subscriber::EnvFilter;

Expand Down Expand Up @@ -198,20 +205,61 @@ async fn main() -> Result<(), anyhow::Error> {

info!("Starknet Devnet listening on {}", address);

if starknet_config.dump_on == Some(DumpOn::Exit) {
server.with_graceful_shutdown(shutdown_signal(api.clone())).await?
} else {
server.await?
let mut tasks = vec![];

if let BlockGenerationOn::Interval(seconds) = starknet_config.block_generation_on {
// use JoinHandle to run block interval creation as a task
let block_interval_handle = task::spawn(create_block_interval(api.clone(), seconds));

tasks.push(block_interval_handle);
}

// run server also as a JoinHandle
let server_handle =
task::spawn(server.with_graceful_shutdown(shutdown_signal(api.clone())).into_future());
tasks.push(server_handle);

// wait for ctrl + c signal (SIGINT)
shutdown_signal(api.clone()).await;

// join all tasks
let results = join_all(tasks).await;

// handle the results of the tasks
for result in results {
result??;
}

Ok(())
}

async fn create_block_interval(api: Api, block_interval: u64) -> Result<(), std::io::Error> {
let mut interval = interval(Duration::from_secs(block_interval));
let mut sigint = signal(SignalKind::interrupt()).expect("Failed to setup SIGINT handler");

loop {
tokio::select! {
_ = interval.tick() => {
let mut starknet = api.starknet.write().await;
info!("Generating block on time interval");

starknet.create_block_dump_event(None).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?
}
_ = sigint.recv() => {
return Ok(())
}
}
}
}

pub async fn shutdown_signal(api: Api) {
tokio::signal::ctrl_c().await.expect("Failed to install CTRL+C signal handler");

// dump on exit scenario
let starknet = api.starknet.read().await;
starknet.dump_events().expect("Failed to dump starknet transactions");
if starknet.config.dump_on == Some(DumpOn::Exit) {
starknet.dump_events().expect("Failed to dump starknet transactions");
}
}

#[cfg(test)]
Expand Down
5 changes: 3 additions & 2 deletions crates/starknet-devnet/tests/general_integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ mod general_integration_tests {
"timeout": 121,
"request_body_size_limit": 1000,
},
"blocks_on_demand": true,
"block_generation_on": "demand",
"lite_mode": false,
"disable_account_impersonation": false,
});
Expand Down Expand Up @@ -112,7 +112,8 @@ mod general_integration_tests {
&expected_config["dump_on"].as_str().unwrap(),
"--dump-path",
&expected_config["dump_path"].as_str().unwrap(),
"--blocks-on-demand",
"--block-generation-on",
"demand",
"--state-archive-capacity",
&expected_config["state_archive"].as_str().unwrap(),
"--host",
Expand Down
18 changes: 10 additions & 8 deletions crates/starknet-devnet/tests/test_advancing_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,10 +280,11 @@ mod advancing_time_tests {
}

#[tokio::test]
async fn set_time_in_past_block_on_demand_mode() {
let devnet = BackgroundDevnet::spawn_with_additional_args(&["--blocks-on-demand"])
.await
.expect("Could not start Devnet");
async fn set_time_in_past_block_generation_on_demand() {
let devnet =
BackgroundDevnet::spawn_with_additional_args(&["--block-generation-on", "demand"])
.await
.expect("Could not start Devnet");

set_time_in_past(&devnet).await;
}
Expand Down Expand Up @@ -322,10 +323,11 @@ mod advancing_time_tests {
}

#[tokio::test]
async fn set_time_in_future_block_on_demand_mode() {
let devnet = BackgroundDevnet::spawn_with_additional_args(&["--blocks-on-demand"])
.await
.expect("Could not start Devnet");
async fn set_time_in_future_block_generation_on_demand() {
let devnet =
BackgroundDevnet::spawn_with_additional_args(&["--block-generation-on", "demand"])
.await
.expect("Could not start Devnet");

set_time_in_future(&devnet).await;
}
Expand Down
Loading

0 comments on commit 24ed52d

Please sign in to comment.