Skip to content
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

Add set time generate_block option #362

Merged
merged 16 commits into from
Feb 29, 2024
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,9 @@ Response:

## Advancing time

Block timestamp can be manipulated by setting the exact time or setting the time offset. Timestamps methods `/set_time` and `/increase_time` will generate a new block. All values should be set in Unix time seconds [Unix time seconds](https://en.wikipedia.org/wiki/Unix_time).
Block timestamp can be manipulated by setting the exact time or setting the time offset. By default, timestamp methods `/set_time` and `/increase_time` generate a new block. This can be changed for `/set_time` by setting the optional parameter `generate_block` to `false`. This skips immediate new block generation, but will use the specified timestamp whenever the next block is supposed to be generated.

All values should be set in [Unix time seconds](https://en.wikipedia.org/wiki/Unix_time).

### Set time

Expand All @@ -294,6 +296,16 @@ POST /set_time
}
```

Doesn't generate a new block, but sets the exact time for the next generated block.

```
POST /set_time
{
"time": TIME_IN_SECONDS,
"generate_block": false
}
```

Warning: block time can be set in the past which might lead to unexpected behavior!

### Increase time
Expand Down
4 changes: 2 additions & 2 deletions crates/starknet-devnet-core/src/starknet/dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ impl Starknet {
self.add_l1_handler_transaction(tx)?;
}
DumpEvent::CreateBlock => {
self.create_block_dump_event(None, None)?;
self.create_block_dump_event(None)?;
}
DumpEvent::SetTime(timestamp) => {
self.set_time(timestamp)?;
self.set_time(timestamp, false)?;
}
DumpEvent::IncreaseTime(time_shift) => {
self.increase_time(time_shift)?;
Expand Down
6 changes: 3 additions & 3 deletions crates/starknet-devnet-core/src/starknet/get_class_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ mod tests {
fn get_class_hash_at_generated_accounts() {
let (mut starknet, account) = setup(Some(100000000), StateArchiveCapacity::Full);

starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();

let block_number = starknet.get_latest_block().unwrap().block_number();
let block_id = BlockId::Number(block_number.0);
Expand All @@ -139,7 +139,7 @@ mod tests {
fn get_class_hash_at_generated_accounts_without_state_archive() {
let (mut starknet, account) = setup(Some(100000000), StateArchiveCapacity::None);

starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();

let block_number = starknet.get_latest_block().unwrap().block_number();
let block_id = BlockId::Number(block_number.0);
Expand All @@ -155,7 +155,7 @@ mod tests {
fn get_class_at_generated_accounts() {
let (mut starknet, account) = setup(Some(100000000), StateArchiveCapacity::Full);

starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();

let block_number = starknet.get_latest_block().unwrap().block_number();
let block_id = BlockId::Number(block_number.0);
Expand Down
90 changes: 57 additions & 33 deletions crates/starknet-devnet-core/src/starknet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ pub struct Starknet {
pub transactions: StarknetTransactions,
pub config: StarknetConfig,
pub pending_block_timestamp_shift: i64,
pub next_block_timestamp: Option<u64>,
pub(crate) messaging: MessagingBroker,
pub(crate) dump_events: Vec<DumpEvent>,
}
Expand All @@ -111,6 +112,7 @@ impl Default for Starknet {
transactions: Default::default(),
config: Default::default(),
pending_block_timestamp_shift: 0,
next_block_timestamp: None,
messaging: Default::default(),
dump_events: Default::default(),
}
Expand Down Expand Up @@ -185,6 +187,7 @@ impl Starknet {
transactions: StarknetTransactions::default(),
config: config.clone(),
pending_block_timestamp_shift: 0,
next_block_timestamp: None,
messaging: Default::default(),
dump_events: Default::default(),
};
Expand Down Expand Up @@ -225,13 +228,25 @@ impl Starknet {
Ok(())
}

fn next_block_timestamp(&mut self) -> BlockTimestamp {
match self.next_block_timestamp {
Some(timestamp) => {
self.next_block_timestamp = None;
BlockTimestamp(timestamp)
}
None => BlockTimestamp(
(Starknet::get_unix_timestamp_as_seconds() as i64
+ self.pending_block_timestamp_shift) as u64,
),
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// Transfer data from pending block into new block and save it to blocks collection
/// Generates new pending block
/// Returns the new block number
pub(crate) fn generate_new_block(
&mut self,
state_diff: StateDiff,
timestamp: Option<u64>,
) -> DevnetResult<BlockNumber> {
let mut new_block = self.pending_block().clone();

Expand All @@ -240,13 +255,7 @@ impl Starknet {
new_block.status = BlockStatus::AcceptedOnL2;

// set block timestamp and context block timestamp for contract execution
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
let block_timestamp = match timestamp {
Some(timestamp) => BlockTimestamp(timestamp),
None => BlockTimestamp(
(Starknet::get_unix_timestamp_as_seconds() as i64
+ self.pending_block_timestamp_shift) as u64,
),
};
let block_timestamp = self.next_block_timestamp();
new_block.set_timestamp(block_timestamp);
self.block_context.block_timestamp = block_timestamp;

Expand Down Expand Up @@ -411,7 +420,7 @@ impl Starknet {
// make cached state part of "persistent" state
self.state.clear_dirty_state();
// create new block from pending one
self.generate_new_block(state_diff, None)?;
self.generate_new_block(state_diff)?;

Ok(())
}
Expand Down Expand Up @@ -939,18 +948,17 @@ impl Starknet {
}

/// create new block from pending one
pub fn create_block(&mut self, timestamp: Option<u64>) -> DevnetResult<(), Error> {
self.generate_new_block(StateDiff::default(), timestamp)?;
pub fn create_block(&mut self) -> DevnetResult<(), Error> {
self.generate_new_block(StateDiff::default())?;
Ok(())
}

// Create block and add DumpEvent
pub fn create_block_dump_event(
&mut self,
timestamp: Option<u64>,
dump_event: Option<DumpEvent>,
) -> DevnetResult<(), Error> {
self.create_block(timestamp)?;
self.create_block()?;

// handle custom event if provided e.g. SetTime, IncreaseTime, otherwise log create block
// events
Expand All @@ -962,25 +970,41 @@ impl Starknet {
Ok(())
}

// Create empty block
pub fn set_time(&mut self, timestamp: u64) -> DevnetResult<(), Error> {
// Set time and optionally create a new block
pub fn set_time(&mut self, timestamp: u64, create_block: bool) -> DevnetResult<(), Error> {
self.set_block_timestamp_shift(
timestamp as i64 - Starknet::get_unix_timestamp_as_seconds() as i64,
);
self.create_block_dump_event(Some(timestamp), Some(DumpEvent::SetTime(timestamp)))

if create_block {
self.set_next_block_timestamp(timestamp);
self.create_block()?;
self.handle_dump_event(DumpEvent::SetTime(timestamp))?;
self.handle_dump_event(DumpEvent::CreateBlock)?;
} else {
self.set_next_block_timestamp(timestamp);
self.handle_dump_event(DumpEvent::SetTime(timestamp))?;
}

Ok(())
}

// Set timestamp shift and create empty block
pub fn increase_time(&mut self, time_shift: u64) -> DevnetResult<(), Error> {
self.set_block_timestamp_shift(self.pending_block_timestamp_shift + time_shift as i64);
self.create_block_dump_event(None, Some(DumpEvent::IncreaseTime(time_shift)))
self.create_block_dump_event(Some(DumpEvent::IncreaseTime(time_shift)))
}

// Set timestamp shift for next blocks
pub fn set_block_timestamp_shift(&mut self, timestamp: i64) {
self.pending_block_timestamp_shift = timestamp;
}

// Set next block timestamp
pub fn set_next_block_timestamp(&mut self, timestamp: u64) {
self.next_block_timestamp = Some(timestamp);
}

pub fn get_unix_timestamp_as_seconds() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
Expand Down Expand Up @@ -1076,7 +1100,7 @@ mod tests {
// blocks collection is empty
assert!(starknet.blocks.num_to_block.is_empty());

starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();
// blocks collection should not be empty
assert!(!starknet.blocks.num_to_block.is_empty());

Expand Down Expand Up @@ -1152,7 +1176,7 @@ mod tests {
let config =
StarknetConfig { state_archive: StateArchiveCapacity::Full, ..Default::default() };
let mut starknet = Starknet::new(&config).unwrap();
starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();

match starknet.get_state_at(&BlockId::Hash(Felt::from(0).into())) {
Err(Error::NoBlock) => (),
Expand All @@ -1165,7 +1189,7 @@ mod tests {
let config =
StarknetConfig { state_archive: StateArchiveCapacity::Full, ..Default::default() };
let mut starknet = Starknet::new(&config).unwrap();
starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();
starknet.blocks.num_to_state.remove(&BlockNumber(0));

match starknet.get_state_at(&BlockId::Number(0)) {
Expand All @@ -1178,7 +1202,7 @@ mod tests {
fn getting_state_at_without_state_archive() {
let config = StarknetConfig::default();
let mut starknet = Starknet::new(&config).unwrap();
starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();

match starknet.get_state_at(&BlockId::Number(0)) {
Err(Error::StateHistoryDisabled) => (),
Expand Down Expand Up @@ -1283,7 +1307,7 @@ mod tests {

starknet.get_latest_block().err().unwrap();

starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();

// last added block number -> 0
let added_block = starknet.blocks.num_to_block.get(&BlockNumber(0)).unwrap();
Expand All @@ -1292,7 +1316,7 @@ mod tests {

assert_eq!(block_number.0, added_block.header.block_number.0);

starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();

let added_block2 = starknet.blocks.num_to_block.get(&BlockNumber(1)).unwrap();
let block_number2 = starknet.get_latest_block().unwrap().block_number();
Expand All @@ -1305,7 +1329,7 @@ mod tests {
let config = StarknetConfig::default();
let mut starknet = Starknet::new(&config).unwrap();

starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();

let num_no_transactions = starknet.get_block_txs_count(&BlockId::Number(0));

Expand All @@ -1316,7 +1340,7 @@ mod tests {
// add transaction hash to pending block
starknet.blocks.pending_block.add_transaction(tx.transaction_hash);

starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();

let num_one_transaction = starknet.get_block_txs_count(&BlockId::Number(1));

Expand All @@ -1341,7 +1365,7 @@ mod tests {
.expect("Could not start Devnet");

// generate initial block with empty state
starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();

// **generate second block**
// add data to state
Expand All @@ -1351,7 +1375,7 @@ mod tests {
// move data from pending_state to state
starknet.state.apply_state_difference(state_diff.clone()).unwrap();
// generate new block and save the state
let second_block = starknet.generate_new_block(state_diff, None).unwrap();
let second_block = starknet.generate_new_block(state_diff).unwrap();

// **generate third block**
// add data to state
Expand All @@ -1361,7 +1385,7 @@ mod tests {
// move data from pending_state to state
starknet.state.apply_state_difference(state_diff.clone()).unwrap();
// generate new block and save the state
let third_block = starknet.generate_new_block(state_diff, None).unwrap();
let third_block = starknet.generate_new_block(state_diff).unwrap();

// check modified state at block 1 and 2 to contain the correct value for the nonce
let second_block_address_nonce = starknet
Expand Down Expand Up @@ -1396,9 +1420,9 @@ mod tests {
let config = StarknetConfig::default();
let mut starknet = Starknet::new(&config).unwrap();

starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();

let latest_block = starknet.get_latest_block();

Expand All @@ -1409,7 +1433,7 @@ mod tests {
let config = StarknetConfig::default();
let mut starknet = Starknet::new(&config).unwrap();

starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();
starknet
.blocks
.pending_block
Expand All @@ -1418,7 +1442,7 @@ mod tests {

let sleep_duration_secs = 5;
thread::sleep(Duration::from_secs(sleep_duration_secs));
starknet.generate_new_block(StateDiff::default(), None).unwrap();
starknet.generate_new_block(StateDiff::default()).unwrap();

let block_timestamp = starknet.get_latest_block().unwrap().header.timestamp;
// check if the pending_block_timestamp is less than the block_timestamp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub async fn create_block(
) -> HttpApiResult<Json<CreatedBlock>> {
let mut starknet = state.api.starknet.write().await;
starknet
.create_block_dump_event(None, None)
.create_block_dump_event(None)
.map_err(|err| HttpApiError::CreateEmptyBlockError { msg: err.to_string() })?;

let last_block = starknet.get_latest_block();
Expand Down
16 changes: 11 additions & 5 deletions crates/starknet-devnet-server/src/api/http/endpoints/time.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
use axum::{Extension, Json};

use crate::api::http::error::HttpApiError;
use crate::api::http::models::{IncreaseTimeResponse, SetTimeResponse, Time};
use crate::api::http::models::{IncreaseTime, IncreaseTimeResponse, SetTime, SetTimeResponse};
use crate::api::http::{HttpApiHandler, HttpApiResult};

pub async fn set_time(
Json(data): Json<Time>,
Json(data): Json<SetTime>,
Extension(state): Extension<HttpApiHandler>,
) -> HttpApiResult<Json<SetTimeResponse>> {
let mut starknet = state.api.starknet.write().await;
let generate_block = data.generate_block.unwrap_or(true);

starknet
.set_time(data.time)
.set_time(data.time, generate_block)
.map_err(|err| HttpApiError::BlockSetTimeError { msg: err.to_string() })?;

if !generate_block {
return Ok(Json(SetTimeResponse { block_timestamp: data.time, block_hash: None }));
}

let last_block = starknet.get_latest_block();
match last_block {
Ok(block) => Ok(Json(SetTimeResponse {
block_timestamp: block.timestamp().0,
block_hash: block.block_hash(),
block_hash: Some(block.block_hash()),
})),
Err(err) => Err(HttpApiError::CreateEmptyBlockError { msg: err.to_string() }),
}
}

pub async fn increase_time(
Json(data): Json<Time>,
Json(data): Json<IncreaseTime>,
Extension(state): Extension<HttpApiHandler>,
) -> HttpApiResult<Json<IncreaseTimeResponse>> {
let mut starknet = state.api.starknet.write().await;
Expand Down
Loading