Skip to content
Open
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `margin` is the value of the new CLI option `--compiler.concurrency-memory-margin-mib`, which defaults to 4GiB,
- `compiler_max_memory_usage` is the value of the `--compiler.max-memory-usage-mib` CLI option, which defaults to 4GiB.

### Removed

- Support for JSON-RPC API versions v0.8 and lower has been removed. The `/rpc/v0_6`, `/rpc/v0_7`, and `/rpc/v0_8` endpoints (and their `/ws` equivalents) are no longer served. The lowest supported version is now v0.9, which is also the new default for the root (`/`) path. The default value for `--rpc.root-version` is now `v09`.

## [0.22.5] - 2026-06-08

### Added
Expand Down
52 changes: 26 additions & 26 deletions crates/load-test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,98 +21,98 @@ fn register_scripted(attack: GooseAttack) -> GooseAttack {

attack
.register_scenario(
scenario!("v08_scripted_mainnet").register_transaction(transaction!(mainnet_scripted)),
scenario!("v09_scripted_mainnet").register_transaction(transaction!(mainnet_scripted)),
)
.register_scenario(
scenario!("v08_scripted_mainnet_without_huge_calls")
scenario!("v09_scripted_mainnet_without_huge_calls")
.register_transaction(transaction!(mainnet_scripted_without_huge_calls)),
)
}

fn register_v08(attack: GooseAttack) -> GooseAttack {
use tasks::v08::*;
fn register_v09(attack: GooseAttack) -> GooseAttack {
use tasks::v09::*;

attack
// primitive operations using the database
.register_scenario(
scenario!("v08_block_by_number")
scenario!("v09_block_by_number")
.register_transaction(transaction!(task_block_by_number)),
)
.register_scenario(
scenario!("v08_block_by_hash").register_transaction(transaction!(task_block_by_hash)),
scenario!("v09_block_by_hash").register_transaction(transaction!(task_block_by_hash)),
)
.register_scenario(
scenario!("v08_state_update_by_hash")
scenario!("v09_state_update_by_hash")
.register_transaction(transaction!(task_state_update_by_hash)),
)
.register_scenario(
scenario!("v08_get_class").register_transaction(transaction!(task_class_by_hash)),
scenario!("v09_get_class").register_transaction(transaction!(task_class_by_hash)),
)
.register_scenario(
scenario!("v08_get_class_hash_at")
scenario!("v09_get_class_hash_at")
.register_transaction(transaction!(task_class_hash_at)),
)
.register_scenario(
scenario!("v08_get_class_at").register_transaction(transaction!(task_class_at)),
scenario!("v09_get_class_at").register_transaction(transaction!(task_class_at)),
)
.register_scenario(
scenario!("v08_block_transaction_count_by_hash")
scenario!("v09_block_transaction_count_by_hash")
.register_transaction(transaction!(task_block_transaction_count_by_hash)),
)
.register_scenario(
scenario!("v08_block_transaction_count_by_number")
scenario!("v09_block_transaction_count_by_number")
.register_transaction(transaction!(task_block_transaction_count_by_number)),
)
.register_scenario(
scenario!("v08_transaction_by_hash")
scenario!("v09_transaction_by_hash")
.register_transaction(transaction!(task_transaction_by_hash)),
)
.register_scenario(
scenario!("v08_transaction_by_block_number_and_index")
scenario!("v09_transaction_by_block_number_and_index")
.register_transaction(transaction!(task_transaction_by_block_number_and_index)),
)
.register_scenario(
scenario!("v08_transaction_by_block_hash_and_index")
scenario!("v09_transaction_by_block_hash_and_index")
.register_transaction(transaction!(task_transaction_by_block_hash_and_index)),
)
.register_scenario(
scenario!("v08_transaction_receipt_by_hash")
scenario!("v09_transaction_receipt_by_hash")
.register_transaction(transaction!(task_transaction_receipt_by_hash)),
)
.register_scenario(
scenario!("v08_block_number").register_transaction(transaction!(task_block_number)),
scenario!("v09_block_number").register_transaction(transaction!(task_block_number)),
)
.register_scenario(
scenario!("v08_get_events").register_transaction(transaction!(task_get_events)),
scenario!("v09_get_events").register_transaction(transaction!(task_get_events)),
)
.register_scenario(
scenario!("v08_get_storage_at").register_transaction(transaction!(task_get_storage_at)),
scenario!("v09_get_storage_at").register_transaction(transaction!(task_get_storage_at)),
)
.register_scenario(
scenario!("v08_get_nonce").register_transaction(transaction!(task_get_nonce)),
scenario!("v09_get_nonce").register_transaction(transaction!(task_get_nonce)),
)
// primitive operations that don't use the database
.register_scenario(
scenario!("v08_syncing").register_transaction(transaction!(task_syncing)),
scenario!("v09_syncing").register_transaction(transaction!(task_syncing)),
)
.register_scenario(
scenario!("v08_chain_id").register_transaction(transaction!(task_chain_id)),
scenario!("v09_chain_id").register_transaction(transaction!(task_chain_id)),
)
// primitive operation doing execution
.register_scenario(scenario!("v08_call").register_transaction(transaction!(task_call)))
.register_scenario(scenario!("v09_call").register_transaction(transaction!(task_call)))
.register_scenario(
scenario!("v08_estimate_fee").register_transaction(transaction!(task_estimate_fee)),
scenario!("v09_estimate_fee").register_transaction(transaction!(task_estimate_fee)),
)
// composite scenario
.register_scenario(
scenario!("v08_block_explorer").register_transaction(transaction!(block_explorer)),
scenario!("v09_block_explorer").register_transaction(transaction!(block_explorer)),
)
}

#[tokio::main]
async fn main() -> Result<(), GooseError> {
let attack = GooseAttack::initialize()?;
let attack = register_v08(attack);
let attack = register_v09(attack);
let attack = register_scripted(attack);

attack.execute().await?;
Expand Down
2 changes: 1 addition & 1 deletion crates/load-test/src/requests.rs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pub mod v08;
pub mod v09;
Original file line number Diff line number Diff line change
Expand Up @@ -237,20 +237,19 @@ pub async fn call(
"calldata": call_data,
"entry_point_selector": entry_point_selector,
},
"block_id": "pending",
"block_id": "pre_confirmed",
}),
)
.await
}

pub async fn estimate_fee_for_invoke(
user: &mut GooseUser,
contract_address: Felt,
sender_address: Felt,
call_data: &[Felt],
entry_point_selector: Felt,
nonce: Felt,
max_fee: Felt,
at_block: Felt,
) -> MethodResult<FeeEstimate> {
) -> MethodResult<Vec<FeeEstimate>> {
post_jsonrpc_request(
user,
"starknet_estimateFee",
Expand All @@ -260,11 +259,17 @@ pub async fn estimate_fee_for_invoke(
"version": "0x1",
"max_fee": max_fee,
"signature": [],
"contract_address": contract_address,
"nonce": nonce,
"sender_address": sender_address,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looking at the JSON-RPC spec, didn't INVOKE_TXN_V3 use sender_address already in v08? How did this even work?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Well, it didn't:

 ------------------------------------------------------------------------------
 Name                     |   # times run |        # fails |  trans/s |  fail/s
 ------------------------------------------------------------------------------
 1: v08_estimate_fee     
   1:                     |         1,372 |   1,372 (100%) |     1372 |    1372

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Rn it's a valid V1 transaction, which actually works.

It used to be a V0 transaction, wrongly marked as a V1.

"calldata": call_data,
"entry_point_selector": entry_point_selector,
}],
"block_id": {"block_hash": at_block}
// Skip validation so the historical nonce and empty signature don't fail the validate/nonce
// checks - we only want to exercise the execution path under load.
"simulation_flags": ["SKIP_VALIDATE"],
// Estimate against the state just before the transaction was included in a block
// (it was included in block 500000), so the transfer's balance check matches
// the historical state instead of drifting with the tip of the chain.
"block_id": {"block_number": 499999}
}),
)
.await
Expand All @@ -275,7 +280,7 @@ pub async fn get_nonce(user: &mut GooseUser, contract_address: Felt) -> MethodRe
user,
"starknet_getNonce",
json!({
"block_id": "pending",
"block_id": "pre_confirmed",
"contract_address": contract_address
}),
)
Expand All @@ -289,7 +294,7 @@ async fn post_jsonrpc_request<T: DeserializeOwned>(
) -> MethodResult<T> {
let request = jsonrpc_request(method, params);
let response = user
.post_json("/rpc/v0_8", &request)
.post_json("/rpc/v0_9", &request)
.await?
.response
.map_err(|e| Box::new(e.into()))?;
Expand Down
2 changes: 1 addition & 1 deletion crates/load-test/src/tasks.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub mod scripted;
pub mod v08;
pub mod v09;
4 changes: 2 additions & 2 deletions crates/load-test/src/tasks/scripted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use goose::prelude::*;
use serde::de::DeserializeOwned;
use serde::Deserialize;

use crate::requests::v08::*;
use crate::requests::v09::*;

/// Script from Starkware that contain some heavy-weight calls mixed with
/// wallet-like calls.
Expand Down Expand Up @@ -102,7 +102,7 @@ async fn post_request<T: DeserializeOwned>(
request: &'static str,
) -> MethodResult<RpcResult<T>> {
let request_builder = user
.get_request_builder(&GooseMethod::Post, "/rpc/v0_8")?
.get_request_builder(&GooseMethod::Post, "/rpc/v0_9")?
.header("Content-Type", "application/json")
.body(request);
let goose_request = GooseRequest::builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use goose::prelude::*;
use pathfinder_crypto::Felt;
use rand::{Rng, SeedableRng};

use crate::requests::v08::*;
use crate::requests::v09::*;

/// Fetch a random block, then fetch all individual transactions and receipts in
/// the block.
Expand Down Expand Up @@ -173,31 +173,65 @@ pub async fn task_call(user: &mut GooseUser) -> TransactionResult {
}

pub async fn task_estimate_fee(user: &mut GooseUser) -> TransactionResult {
// estimate invoke on a test contract deployed in block 0
// https://voyager.online/contract/0x06ee3440b08a9c805305449ec7f7003f27e9f7e287b83610952ec36bdc5a6bae
// Estimate the fee for a real mainnet INVOKE v1 transaction from block 500k.
// Replay an existing transaction so that the account, the called contract and
// its entry point are all guaranteed to exist on chain.
// `estimate_fee_for_invoke` runs this against the previous state (block 499999)
// with SKIP_VALIDATE, so the historical nonce/signature and the chain tip's
// mutable state (token balances etc.) don't influence the result. We only care
// about execution.
//
// Structure of the transaction:
// - invoke `__execute__` on `sender_address`, passing `calldata` as the
// multicall to do,
// - `calldata` is the Cairo 0 OpenZeppelin account multicall, encoded as:
// `(call_array_len, [(to, selector, data_offset, data_len); N], calldata_len,
// calldata)`.
//
// So a single ERC20-style transfer looks like this:
//
// call_array_len = 1
// call[0].to = 0x49d36570... (token contract)
// call[0].selector = 0x83afd3f4... ("transfer")
// call[0].data_offset = 0 (offset into the flat calldata)
// call[0].data_len = 3 (recipient + u256 amount)
// calldata_len = 3
// calldata[0] = 0x2db7e01c... (recipient address)
// calldata[1] = 0x13717a1765d1800 (amount, u256 low)
// calldata[2] = 0x0 (amount, u256 high)
estimate_fee_for_invoke(
user,
Felt::from_hex_str("0x06ee3440b08a9c805305449ec7f7003f27e9f7e287b83610952ec36bdc5a6bae")
// Sender (account) address
Felt::from_hex_str("0x3a20d4f7b4229e7c4863dab158b4d076d7f454b893d90a62011882dc4caca2a")
.unwrap(),
// Account __execute__ calldata: a single transfer call
&[
// address
Felt::from_hex_str(
"0x01e2cd4b3588e8f6f9c4e89fb0e293bf92018c96d7a93ee367d29a284223b6ff",
)
.unwrap(),
// value
Felt::from_hex_str(
"0x071d1e9d188c784a0bde95c1d508877a0d93e9102b37213d1e13f3ebc54a7751",
)
.unwrap(),
// call_array_len
Felt::from_hex_str("0x1").unwrap(),
// call[0].to (token contract)
Felt::from_hex_str("0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7")
.unwrap(),
// call[0].selector ("transfer")
Felt::from_hex_str("0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e")
.unwrap(),
// call[0].data_offset
Felt::from_hex_str("0x0").unwrap(),
// call[0].data_len
Felt::from_hex_str("0x3").unwrap(),
// calldata_len
Felt::from_hex_str("0x3").unwrap(),
// calldata[0]: recipient address
Felt::from_hex_str("0x2db7e01c69be7e741fcd08fb5096914029131334dbca1d63ab33c05e7a92153")
.unwrap(),
// calldata[1]: amount, u256 low
Felt::from_hex_str("0x13717a1765d1800").unwrap(),
// calldata[2]: amount, u256 high
Felt::from_hex_str("0x0").unwrap(),
],
// "set_value" entry point
Felt::from_hex_str("0x3d7905601c217734671143d457f0db37f7f8883112abd34b92c4abfeafde0c3")
.unwrap(),
// nonce (historical; not checked because of SKIP_VALIDATE)
Felt::from_hex_str("0x121419").unwrap(),
// max_fee
Felt::ZERO,
// hash of mainnet block 0
Felt::from_hex_str("0x47c3637b57c2b079b93c61539950c17e868a28f46cdef28f88521067f21e943")
.unwrap(),
)
.await?;
Ok(())
Expand Down
9 changes: 7 additions & 2 deletions crates/load-test/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ pub struct ContractClass {

#[derive(Clone, Debug, serde::Deserialize, PartialEq, Eq)]
pub struct FeeEstimate {
pub gas_consumed: String,
pub gas_price: String,
pub l1_gas_consumed: String,
pub l1_gas_price: String,
pub l1_data_gas_consumed: String,
pub l1_data_gas_price: String,
pub l2_gas_consumed: String,
pub l2_gas_price: String,
pub overall_fee: String,
pub unit: String,
}
3 changes: 0 additions & 3 deletions crates/pathfinder/src/bin/pathfinder/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,9 +448,6 @@ Hint: This is usually caused by exceeding the file descriptor limit of your syst
};

let default_version = match config.rpc_root_version {
config::RootRpcVersion::V06 => pathfinder_rpc::RpcVersion::V06,
config::RootRpcVersion::V07 => pathfinder_rpc::RpcVersion::V07,
config::RootRpcVersion::V08 => pathfinder_rpc::RpcVersion::V08,
config::RootRpcVersion::V09 => pathfinder_rpc::RpcVersion::V09,
config::RootRpcVersion::V10 => pathfinder_rpc::RpcVersion::V10,
};
Expand Down
5 changes: 1 addition & 4 deletions crates/pathfinder/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ Examples:
#[arg(
long = "rpc.root-version",
long_help = "Version of the JSON-RPC API to serve on the / (root) path",
default_value = "v08",
default_value = "v09",
env = "PATHFINDER_RPC_ROOT_VERSION"
)]
rpc_root_version: RootRpcVersion,
Expand Down Expand Up @@ -697,9 +697,6 @@ The default is suitable for all uses except testing.",

#[derive(clap::ValueEnum, Debug, Clone, Copy, PartialEq)]
pub enum RootRpcVersion {
V06,
V07,
V08,
V09,
V10,
}
Expand Down
Loading
Loading