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

chore(tests_integration): add nonce verification #2071

Merged
merged 2 commits into from
Nov 19, 2024
Merged
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
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.

9 changes: 7 additions & 2 deletions config/sequencer/default_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@
},
"batcher_config.block_builder_config.sequencer_address": {
"description": "The address of the sequencer.",
"privacy": "Public",
"value": "0x0"
"pointer_target": "sequencer_address",
"privacy": "Public"
},
"batcher_config.block_builder_config.tx_chunk_size": {
"description": "The size of the transaction chunk.",
Expand Down Expand Up @@ -899,6 +899,11 @@
"privacy": "Public",
"value": ""
},
"sequencer_address": {
"description": "A required param! The sequencer address.",
"param_type": "String",
"privacy": "TemporaryValue"
},
"strk_fee_token_address": {
"description": "A required param! Address of the STRK fee token.",
"param_type": "String",
Expand Down
1 change: 1 addition & 0 deletions crates/starknet_integration_tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ indexmap.workspace = true
mempool_test_utils.workspace = true
papyrus_common.workspace = true
papyrus_consensus.workspace = true
papyrus_execution.workspace = true
papyrus_network = { workspace = true, features = ["testing"] }
papyrus_protobuf.workspace = true
papyrus_rpc.workspace = true
Expand Down
2 changes: 2 additions & 0 deletions crates/starknet_integration_tests/src/config_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@ pub(crate) fn dump_config_file_changes(
dir: PathBuf,
) -> PathBuf {
// Dump config changes file for the sequencer node.
// TODO(Tsabary): auto dump the entirety of RequiredParams fields.
let json_data = config_fields_to_json!(
required_params.chain_id,
required_params.eth_fee_token_address,
required_params.strk_fee_token_address,
required_params.sequencer_address,
config.rpc_state_reader_config.json_rpc_version,
config.rpc_state_reader_config.url,
config.batcher_config.storage.db_config.path_prefix,
Expand Down
1 change: 1 addition & 0 deletions crates/starknet_integration_tests/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pub async fn create_config(
chain_id,
eth_fee_token_address: fee_token_addresses.eth_fee_token_address,
strk_fee_token_address: fee_token_addresses.strk_fee_token_address,
sequencer_address: ContractAddress::from(1312_u128), // Arbitrary non-zero value.
},
consensus_proposals_channels,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,24 @@ use std::process::Stdio;
use std::time::Duration;

use mempool_test_utils::starknet_api_test_utils::MultiAccountTransactionGenerator;
use papyrus_execution::execution_utils::get_nonce_at;
use papyrus_storage::state::StateStorageReader;
use papyrus_storage::StorageReader;
use rstest::{fixture, rstest};
use starknet_api::block::BlockNumber;
use starknet_api::core::{ContractAddress, Nonce};
use starknet_api::state::StateNumber;
use starknet_integration_tests::integration_test_setup::IntegrationTestSetup;
use starknet_integration_tests::utils::{
create_integration_test_tx_generator,
run_transaction_generator_test_scenario,
};
use starknet_sequencer_infra::trace_util::configure_tracing;
use starknet_types_core::felt::Felt;
use tokio::process::{Child, Command};
use tokio::task::{self, JoinHandle};
use tracing::info;
use tokio::time::interval;
use tracing::{error, info};

#[fixture]
fn tx_generator() -> MultiAccountTransactionGenerator {
Expand All @@ -25,6 +33,7 @@ async fn spawn_node_child_task(node_config_path: PathBuf) -> Child {
// Get the current working directory for the project
let project_path = env::current_dir().expect("Failed to get current directory").join("../..");

// TODO(Tsabary): Capture output to a log file, and present it in case of a failure.
// TODO(Tsabary): Change invocation from "cargo run" to separate compilation and invocation
// (build, and then invoke the binary).
Command::new("cargo")
Expand Down Expand Up @@ -54,9 +63,68 @@ async fn spawn_run_node(node_config_path: PathBuf) -> JoinHandle<()> {
})
}

/// Reads the latest block number from the storage.
fn get_latest_block_number(storage_reader: &StorageReader) -> BlockNumber {
let txn = storage_reader.begin_ro_txn().unwrap();
txn.get_state_marker()
.expect("There should always be a state marker")
.prev()
.expect("There should be a previous block in the storage, set by the test setup")
}

/// Reads an account nonce after a block number from storage.
fn get_account_nonce(
storage_reader: &StorageReader,
block_number: BlockNumber,
contract_address: ContractAddress,
) -> Nonce {
let txn = storage_reader.begin_ro_txn().unwrap();
let state_number = StateNumber::unchecked_right_after_block(block_number);
get_nonce_at(&txn, state_number, None, contract_address)
.expect("Should always be Ok(Some(Nonce))")
.expect("Should always be Some(Nonce)")
}

/// Sample a storage until sufficiently many blocks have been stored. Returns an error if after
/// the given number of attempts the target block number has not been reached.
async fn await_block(
interval_duration: Duration,
target_block_number: BlockNumber,
max_attempts: usize,
storage_reader: &StorageReader,
) -> Result<(), ()> {
let mut interval = interval(interval_duration);
let mut count = 0;
loop {
// Read the latest block number.
let latest_block_number = get_latest_block_number(storage_reader);
count += 1;

// Check if reached the target block number.
if latest_block_number >= target_block_number {
info!("Found block {} after {} queries.", target_block_number, count);
return Ok(());
}

// Check if reached the maximum attempts.
if count > max_attempts {
error!(
"Latest block is {}, expected {}, stopping sampling.",
latest_block_number, target_block_number
);
return Err(());
}

// Wait for the next interval.
interval.tick().await;
}
}

#[rstest]
#[tokio::test]
async fn test_end_to_end_integration(tx_generator: MultiAccountTransactionGenerator) {
const EXPECTED_BLOCK_NUMBER: BlockNumber = BlockNumber(15);

configure_tracing();
info!("Running integration test setup.");

Expand All @@ -81,13 +149,34 @@ async fn test_end_to_end_integration(tx_generator: MultiAccountTransactionGenera

let n_txs = 50;
info!("Sending {n_txs} txs.");
run_transaction_generator_test_scenario(tx_generator, n_txs, send_rpc_tx_fn).await;
let (tx_hashes, sender_address) =
run_transaction_generator_test_scenario(tx_generator, n_txs, send_rpc_tx_fn).await;

info!("Awaiting until {EXPECTED_BLOCK_NUMBER} blocks have been created.");

info!("Shutting down.");
let (batcher_storage_reader, _) =
papyrus_storage::open_storage(integration_test_setup.batcher_storage_config)
.expect("Failed to open batcher's storage");

match await_block(Duration::from_secs(5), EXPECTED_BLOCK_NUMBER, 15, &batcher_storage_reader)
.await
{
Ok(_) => {}
Err(_) => panic!("Did not reach expected block number."),
}

info!("Shutting the node down.");
node_run_handle.abort();
let res = node_run_handle.await;
assert!(
res.expect_err("Node should have been stopped.").is_cancelled(),
"Node should have been stopped."
);

info!("Verifying tx sender account nonce.");
let expected_nonce_value = tx_hashes.len() + 1;
let expected_nonce =
Nonce(Felt::from_hex_unchecked(format!("0x{:X}", expected_nonce_value).as_str()));
let nonce = get_account_nonce(&batcher_storage_reader, EXPECTED_BLOCK_NUMBER, sender_address);
assert_eq!(nonce, expected_nonce);
}
9 changes: 9 additions & 0 deletions crates/starknet_sequencer_node/src/config/node_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ pub static CONFIG_POINTERS: LazyLock<ConfigPointers> = LazyLock::new(|| {
"gateway_config.chain_info.fee_token_addresses.strk_fee_token_address",
]),
),
// TODO(tsabary): set as a regular required parameter.
(
ser_pointer_target_required_param(
"sequencer_address",
SerializationType::String,
"The sequencer address.",
),
set_pointing_param_paths(&["batcher_config.block_builder_config.sequencer_address"]),
),
];
let mut common_execution_config = generate_struct_pointer(
"versioned_constants_overrides".to_owned(),
Expand Down
4 changes: 4 additions & 0 deletions crates/starknet_sequencer_node/src/config/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub struct RequiredParams {
pub chain_id: ChainId,
pub eth_fee_token_address: ContractAddress,
pub strk_fee_token_address: ContractAddress,
pub sequencer_address: ContractAddress,
}

impl RequiredParams {
Expand All @@ -19,6 +20,7 @@ impl RequiredParams {
chain_id: ChainId::create_for_testing(),
eth_fee_token_address: ContractAddress::from(2_u128),
strk_fee_token_address: ContractAddress::from(3_u128),
sequencer_address: ContractAddress::from(17_u128),
}
}

Expand All @@ -31,6 +33,8 @@ impl RequiredParams {
self.eth_fee_token_address.to_string(),
"--strk_fee_token_address".to_string(),
self.strk_fee_token_address.to_string(),
"--sequencer_address".to_string(),
self.sequencer_address.to_string(),
];
// Verify all arguments and their values are present.
assert!(
Expand Down
Loading