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

Update of getBlockWithTxHashes and getStateUpdate #461

Merged
merged 12 commits into from
May 9, 2024
25 changes: 24 additions & 1 deletion crates/starknet-devnet-core/src/blocks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use starknet_api::stark_felt;
use starknet_rs_core::types::{BlockId, BlockTag};
use starknet_types::contract_address::ContractAddress;
use starknet_types::felt::{BlockHash, Felt, TransactionHash};
use starknet_types::rpc::block::{BlockHeader as TypesBlockHeader, ResourcePrice};
use starknet_types::rpc::block::{
BlockHeader as TypesBlockHeader, PendingBlockHeader as TypesPendingBlockHeader, ResourcePrice,
};
use starknet_types::traits::HashProducer;

use crate::constants::STARKNET_VERSION;
Expand Down Expand Up @@ -172,6 +174,27 @@ pub struct StarknetBlock {
pub(crate) status: BlockStatus,
}

impl From<&StarknetBlock> for TypesPendingBlockHeader {
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
fn from(value: &StarknetBlock) -> Self {
Self {
parent_hash: value.parent_hash(),
sequencer_address: value.sequencer_address(),
new_root: value.new_root(),
timestamp: value.timestamp(),
starknet_version: STARKNET_VERSION.to_string(),
l1_gas_price: ResourcePrice {
price_in_fri: value.header.l1_gas_price.price_in_fri.0.into(),
price_in_wei: value.header.l1_gas_price.price_in_wei.0.into(),
},
l1_data_gas_price: ResourcePrice {
price_in_fri: value.header.l1_data_gas_price.price_in_fri.0.into(),
price_in_wei: value.header.l1_data_gas_price.price_in_wei.0.into(),
},
l1_da_mode: value.header.l1_da_mode,
}
}
}

impl From<&StarknetBlock> for TypesBlockHeader {
fn from(value: &StarknetBlock) -> Self {
Self {
Expand Down
81 changes: 52 additions & 29 deletions crates/starknet-devnet-server/src/api/json_rpc/endpoints.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use starknet_core::error::{Error, StateError};
use starknet_rs_core::types::{BlockId as ImportedBlockId, MsgFromL1};
use starknet_rs_core::types::{BlockId as ImportedBlockId, BlockTag, MsgFromL1};
use starknet_types::contract_address::ContractAddress;
use starknet_types::felt::{ClassHash, TransactionHash};
use starknet_types::patricia_key::PatriciaKey;
use starknet_types::rpc::block::{Block, BlockHeader, BlockId};
use starknet_types::rpc::state::StateUpdate;
use starknet_types::rpc::block::{Block, BlockHeader, BlockId, PendingBlock, PendingBlockHeader};
use starknet_types::rpc::state::{PendingStateUpdate, StateUpdate};
use starknet_types::rpc::transactions::{
BroadcastedTransaction, EventFilter, EventsChunk, FunctionCall, SimulationFlag,
};
Expand All @@ -24,21 +24,33 @@ impl JsonRpcHandler {

/// starknet_getBlockWithTxHashes
pub async fn get_block_with_tx_hashes(&self, block_id: BlockId) -> StrictRpcResult {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about get_block_with_txs and get_block_with_receipts

let block =
self.api.starknet.read().await.get_block(block_id.as_ref()).map_err(
|err| match err {
Error::NoBlock => ApiError::BlockNotFound,
unknown_error => ApiError::StarknetDevnetError(unknown_error),
},
)?;
let starknet = self.api.starknet.read().await;

Ok(StarknetResponse::Block(Block {
status: *block.status(),
header: BlockHeader::from(&block),
transactions: starknet_types::rpc::transactions::Transactions::Hashes(
block.get_transactions().to_owned(),
),
}))
let block = starknet.get_block(block_id.as_ref()).map_err(|err| match err {
Error::NoBlock => ApiError::BlockNotFound,
unknown_error => ApiError::StarknetDevnetError(unknown_error),
})?;

// StarknetBlock needs to be mapped to PendingBlock response only in blocks_on_demand mode
// and when block_id is pending
if starknet.config.blocks_on_demand
mikiw marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this logic could be moved in Starknet-core project

&& block_id == ImportedBlockId::Tag(BlockTag::Pending).into()
{
Ok(StarknetResponse::PendingBlock(PendingBlock {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StarknetResponse::Block can receive an enum as its inner property. For example:

enum StarknetResponse{
Block(Block)
}

enum Block {
Block,
PendingBlock
}

header: PendingBlockHeader::from(&block),
transactions: starknet_types::rpc::transactions::Transactions::Hashes(
block.get_transactions().to_owned(),
),
}))
} else {
Ok(StarknetResponse::Block(Block {
status: *block.status(),
header: BlockHeader::from(&block),
transactions: starknet_types::rpc::transactions::Transactions::Hashes(
block.get_transactions().to_owned(),
),
}))
}
}

/// starknet_getBlockWithTxs
Expand Down Expand Up @@ -71,22 +83,33 @@ impl JsonRpcHandler {

/// starknet_getStateUpdate
pub async fn get_state_update(&self, block_id: BlockId) -> StrictRpcResult {
let starknet = self.api.starknet.read().await;

let state_update =
self.api.starknet.read().await.block_state_update(block_id.as_ref()).map_err(
|err| match err {
Error::NoBlock => ApiError::BlockNotFound,
unknown_error => ApiError::StarknetDevnetError(unknown_error),
},
)?;
starknet.block_state_update(block_id.as_ref()).map_err(|err| match err {
Error::NoBlock => ApiError::BlockNotFound,
unknown_error => ApiError::StarknetDevnetError(unknown_error),
})?;

let state_diff = state_update.state_diff.into();

Ok(StarknetResponse::StateUpdate(StateUpdate {
block_hash: state_update.block_hash,
new_root: state_update.new_root,
old_root: state_update.old_root,
state_diff,
}))
// StateUpdate needs to be mapped to PendingStateUpdate response only in blocks_on_demand
// mode and when block_id is pending
if starknet.config.blocks_on_demand
mikiw marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same here, can be moved into Starknet-devnet-core crate.

&& block_id == ImportedBlockId::Tag(BlockTag::Pending).into()
{
Ok(StarknetResponse::PendingStateUpdate(PendingStateUpdate {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StarknetResponse::StateUpdate enum variant can receive an enum as its inner part, similar to StarknetResponse::TransactionTrace

old_root: state_update.old_root,
state_diff,
}))
} else {
Ok(StarknetResponse::StateUpdate(StateUpdate {
block_hash: state_update.block_hash,
new_root: state_update.new_root,
old_root: state_update.old_root,
state_diff,
}))
}
}

/// starknet_getStorageAt
Expand Down
6 changes: 4 additions & 2 deletions crates/starknet-devnet-server/src/api/json_rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ use models::{
use serde::{Deserialize, Serialize};
use starknet_rs_core::types::ContractClass as CodegenContractClass;
use starknet_types::felt::Felt;
use starknet_types::rpc::block::Block;
use starknet_types::rpc::block::{Block, PendingBlock};
use starknet_types::rpc::estimate_message_fee::{
EstimateMessageFeeRequestWrapper, FeeEstimateWrapper,
};
use starknet_types::rpc::state::StateUpdate;
use starknet_types::rpc::state::{PendingStateUpdate, StateUpdate};
use starknet_types::rpc::transaction_receipt::TransactionReceipt;
use starknet_types::rpc::transactions::{
BlockTransactionTrace, EventsChunk, SimulatedTransaction, TransactionTrace, TransactionWithHash,
Expand Down Expand Up @@ -353,7 +353,9 @@ impl std::fmt::Display for StarknetRequest {
#[serde(untagged)]
pub enum StarknetResponse {
Block(Block),
PendingBlock(PendingBlock),
StateUpdate(StateUpdate),
PendingStateUpdate(PendingStateUpdate),
Felt(Felt),
Transaction(TransactionWithHash),
TransactionReceiptByTransactionHash(Box<TransactionReceipt>),
Expand Down
20 changes: 20 additions & 0 deletions crates/starknet-devnet-types/src/rpc/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ pub struct Block {
pub transactions: Transactions,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PendingBlock {
#[serde(flatten)]
pub header: PendingBlockHeader,
pub transactions: Transactions,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct BlockHeader {
Expand All @@ -84,6 +92,18 @@ pub struct BlockHeader {
pub l1_da_mode: L1DataAvailabilityMode,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct PendingBlockHeader {
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
pub parent_hash: BlockHash,
pub sequencer_address: ContractAddress,
pub new_root: GlobalRootHex,
pub timestamp: BlockTimestamp,
pub starknet_version: String,
pub l1_gas_price: ResourcePrice,
pub l1_data_gas_price: ResourcePrice,
pub l1_da_mode: L1DataAvailabilityMode,
}
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ResourcePrice {
Expand Down
7 changes: 7 additions & 0 deletions crates/starknet-devnet-types/src/rpc/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ pub struct StateUpdate {
pub state_diff: ThinStateDiff,
}

#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct PendingStateUpdate {
pub old_root: GlobalRootHex,
pub state_diff: ThinStateDiff,
}

#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
pub struct ThinStateDiff {
pub deployed_contracts: Vec<DeployedContract>,
Expand Down
4 changes: 2 additions & 2 deletions crates/starknet-devnet/tests/common/background_devnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,9 @@ impl BackgroundDevnet {

pub async fn get_pending_block_with_tx_hashes(
&self,
) -> Result<BlockWithTxHashes, anyhow::Error> {
) -> Result<MaybePendingBlockWithTxHashes, anyhow::Error> {
match self.json_rpc_client.get_block_with_tx_hashes(BlockId::Tag(BlockTag::Pending)).await {
Ok(MaybePendingBlockWithTxHashes::Block(b)) => Ok(b),
Ok(b) => Ok(b),
other => Err(anyhow::format_err!("Got unexpected block: {other:?}")),
}
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
}
Expand Down
70 changes: 62 additions & 8 deletions crates/starknet-devnet/tests/test_blocks_on_demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ pub mod common;
mod blocks_on_demand_tests {
use std::sync::Arc;

use serde_json::json;
use starknet_rs_accounts::{Account, Call, ExecutionEncoding, SingleOwnerAccount};
use starknet_rs_contract::ContractFactory;
use starknet_rs_core::types::{BlockStatus, BlockTag, FieldElement};
use starknet_rs_core::types::{
BlockStatus, BlockTag, FieldElement, MaybePendingBlockWithTxHashes,
};
use starknet_rs_core::utils::{get_selector_from_name, get_udc_deployed_address};
use starknet_types::rpc::transaction_receipt::FeeUnit;

Expand All @@ -19,6 +22,38 @@ mod blocks_on_demand_tests {
static DUMMY_ADDRESS: u128 = 1;
static DUMMY_AMOUNT: u128 = 1;

async fn assert_pending_state_update(devnet: &BackgroundDevnet) {
let pending_state_update = &devnet
.send_custom_rpc(
"starknet_getStateUpdate",
json!( {
"block_id": "pending"
}),
)
.await["result"];

assert!(pending_state_update["old_root"].is_string());
assert!(pending_state_update["state_diff"].is_object());
assert!(pending_state_update["block_hash"].is_null());
assert!(pending_state_update["new_root"].is_null());
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
}

async fn assert_latest_state_update(devnet: &BackgroundDevnet, block_id: &str) {
let latest_state_update = &devnet
.send_custom_rpc(
"starknet_getStateUpdate",
json!( {
"block_id": block_id
}),
)
.await["result"];

assert!(latest_state_update["block_hash"].is_string());
assert!(latest_state_update["new_root"].is_string());
assert!(latest_state_update["old_root"].is_string());
assert!(latest_state_update["state_diff"].is_object());
}

async fn assert_latest_block_with_transactions(
devnet: &BackgroundDevnet,
block_number: u64,
Expand All @@ -36,13 +71,20 @@ mod blocks_on_demand_tests {

async fn assert_pending_block_with_transactions(
devnet: &BackgroundDevnet,
block_number: u64,
transactions_count: u128,
) {
let pending_block = devnet.get_pending_block_with_tx_hashes().await.unwrap();
assert_eq!(pending_block.block_number, block_number);
assert_eq!(pending_block.transactions.len() as u128, transactions_count);
assert_eq!(pending_block.status, BlockStatus::Pending);

match pending_block {
MaybePendingBlockWithTxHashes::PendingBlock(block) => {
assert_eq!(block.transactions.len() as u128, transactions_count);

for tx_hash in block.transactions {
assert_tx_successful(&tx_hash, &devnet.json_rpc_client).await;
}
}
_ => panic!("Invalid block type {:?}", pending_block),
}
}

async fn assert_balance(devnet: &BackgroundDevnet, expected: FieldElement, tag: BlockTag) {
Expand Down Expand Up @@ -81,11 +123,14 @@ mod blocks_on_demand_tests {
.await;

assert_latest_block_with_transactions(&devnet, 1, tx_hashes).await;
assert_pending_block_with_transactions(&devnet, 2, 0).await;
assert_pending_block_with_transactions(&devnet, 0).await;

assert_pending_state_update(&devnet).await;
assert_latest_state_update(&devnet, "latest").await;
}

#[tokio::test]
async fn pending_block_in_normal_mode() {
async fn pending_block_and_state_in_normal_mode() {
let devnet = BackgroundDevnet::spawn().await.unwrap();

devnet.create_block().await.unwrap();
Expand All @@ -94,7 +139,16 @@ mod blocks_on_demand_tests {
let latest_block = devnet.get_latest_block_with_tx_hashes().await.unwrap();

// querying pending block in normal mode should default to the latest block
assert_eq!(pending_block, latest_block);
match pending_block {
MaybePendingBlockWithTxHashes::Block(block) => {
assert_eq!(block, latest_block);
}
_ => panic!("Invalid block type {:?}", pending_block),
}

// querying state update in normal mode should default to the latest state update
assert_latest_state_update(&devnet, "pending").await;
assert_latest_state_update(&devnet, "latest").await;
}

#[tokio::test]
Expand Down