From 0a4b210d9f85b3194174c4b714e23724c1c3daff Mon Sep 17 00:00:00 2001 From: Yael Doweck Date: Wed, 4 Dec 2024 15:45:51 +0200 Subject: [PATCH] feat(consensus): calculate the executable_transaction in consensus_context --- ..._config_test__dump_default_config.snap.new | 623 ++++++++++++++++++ .../src/sequencer_consensus_context.rs | 22 +- .../src/sequencer_consensus_context_test.rs | 29 +- crates/starknet_api/src/transaction.rs | 15 +- crates/starknet_api/src/transaction_test.rs | 52 +- .../src/consensus_manager.rs | 1 + 6 files changed, 700 insertions(+), 42 deletions(-) create mode 100644 crates/papyrus_node/src/config/snapshots/papyrus_node__config__config_test__dump_default_config.snap.new diff --git a/crates/papyrus_node/src/config/snapshots/papyrus_node__config__config_test__dump_default_config.snap.new b/crates/papyrus_node/src/config/snapshots/papyrus_node__config__config_test__dump_default_config.snap.new new file mode 100644 index 0000000000..ffb53ffc8a --- /dev/null +++ b/crates/papyrus_node/src/config/snapshots/papyrus_node__config__config_test__dump_default_config.snap.new @@ -0,0 +1,623 @@ +--- +source: crates/papyrus_node/src/config/config_test.rs +assertion_line: 104 +expression: dumped_default_config +--- +{ + "base_layer.node_url": { + "description": "A required param! Ethereum node URL. A schema to match to Infura node: https://mainnet.infura.io/v3/, but any other node can be used.", + "param_type": "String", + "privacy": "Private" + }, + "base_layer.starknet_contract_address": { + "description": "Starknet contract address in ethereum.", + "value": "0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4", + "privacy": "Public" + }, + "central.class_cache_size": { + "description": "Size of class cache, must be a positive integer.", + "value": { + "$serde_json::private::Number": "100" + }, + "privacy": "Public" + }, + "central.concurrent_requests": { + "description": "Maximum number of concurrent requests to Starknet feeder-gateway for getting a type of data (for example, blocks).", + "value": { + "$serde_json::private::Number": "10" + }, + "privacy": "Public" + }, + "central.http_headers": { + "description": "'k1:v1 k2:v2 ...' headers for SN-client.", + "value": "", + "privacy": "Private" + }, + "central.max_classes_to_download": { + "description": "Maximum number of classes to download at a given time.", + "value": { + "$serde_json::private::Number": "20" + }, + "privacy": "Public" + }, + "central.max_state_updates_to_download": { + "description": "Maximum number of state updates to download at a given time.", + "value": { + "$serde_json::private::Number": "20" + }, + "privacy": "Public" + }, + "central.max_state_updates_to_store_in_memory": { + "description": "Maximum number of state updates to store in memory at a given time.", + "value": { + "$serde_json::private::Number": "20" + }, + "privacy": "Public" + }, + "central.retry_config.max_retries": { + "description": "Maximum number of retries before the node stops retrying.", + "value": { + "$serde_json::private::Number": "10" + }, + "privacy": "Public" + }, + "central.retry_config.retry_base_millis": { + "description": "Base waiting time after a failed request. After that, the time increases exponentially.", + "value": { + "$serde_json::private::Number": "30" + }, + "privacy": "Public" + }, + "central.retry_config.retry_max_delay_millis": { + "description": "Max waiting time after a failed request.", + "value": { + "$serde_json::private::Number": "30000" + }, + "privacy": "Public" + }, + "central.starknet_url": { + "description": "Starknet feeder-gateway URL. It should match chain_id.", + "value": "https://alpha-mainnet.starknet.io/", + "privacy": "Public" + }, + "collect_profiling_metrics": { + "description": "If true, collect profiling metrics for the node.", + "value": false, + "privacy": "Public" + }, + "consensus.#is_none": { + "description": "Flag for an optional field.", + "value": true, + "privacy": "TemporaryValue" + }, + "consensus.chain_id": { + "description": "The chain id of the Starknet chain.", + "value": "0x0", + "privacy": "Public" + }, + "consensus.consensus_delay": { + "description": "Delay (seconds) before starting consensus to give time for network peering.", + "value": { + "$serde_json::private::Number": "5" + }, + "privacy": "Public" + }, + "consensus.network_config.advertised_multiaddr": { + "description": "The external address other peers see this node. If this is set, the node will not try to find out which addresses it has and will write this address as external instead", + "value": "", + "privacy": "Public" + }, + "consensus.network_config.advertised_multiaddr.#is_none": { + "description": "Flag for an optional field.", + "value": true, + "privacy": "TemporaryValue" + }, + "consensus.network_config.bootstrap_peer_multiaddr": { + "description": "The multiaddress of the peer node. It should include the peer's id. For more info: https://docs.libp2p.io/concepts/fundamentals/peers/", + "value": "", + "privacy": "Public" + }, + "consensus.network_config.bootstrap_peer_multiaddr.#is_none": { + "description": "Flag for an optional field.", + "value": true, + "privacy": "TemporaryValue" + }, + "consensus.network_config.chain_id": { + "description": "The chain to follow. For more details see https://docs.starknet.io/documentation/architecture_and_concepts/Blocks/transactions/#chain-id.", + "value": "SN_MAIN", + "privacy": "Public" + }, + "consensus.network_config.discovery_config.bootstrap_dial_retry_config.base_delay_millis": { + "description": "The base delay in milliseconds for the exponential backoff strategy.", + "value": { + "$serde_json::private::Number": "2" + }, + "privacy": "Public" + }, + "consensus.network_config.discovery_config.bootstrap_dial_retry_config.factor": { + "description": "The factor for the exponential backoff strategy.", + "value": { + "$serde_json::private::Number": "5" + }, + "privacy": "Public" + }, + "consensus.network_config.discovery_config.bootstrap_dial_retry_config.max_delay_seconds": { + "description": "The maximum delay in seconds for the exponential backoff strategy.", + "value": { + "$serde_json::private::Number": "5" + }, + "privacy": "Public" + }, + "consensus.network_config.discovery_config.heartbeat_interval": { + "description": "The interval between each discovery (Kademlia) query in milliseconds.", + "value": { + "$serde_json::private::Number": "100" + }, + "privacy": "Public" + }, + "consensus.network_config.idle_connection_timeout": { + "description": "Amount of time in seconds that a connection with no active sessions will stay alive.", + "value": { + "$serde_json::private::Number": "120" + }, + "privacy": "Public" + }, + "consensus.network_config.peer_manager_config.malicious_timeout_seconds": { + "description": "The duration in seconds a peer is blacklisted after being marked as malicious.", + "value": { + "$serde_json::private::Number": "31536000" + }, + "privacy": "Public" + }, + "consensus.network_config.peer_manager_config.unstable_timeout_millis": { + "description": "The duration in milliseconds a peer blacklisted after being reported as unstable.", + "value": { + "$serde_json::private::Number": "1000" + }, + "privacy": "Public" + }, + "consensus.network_config.quic_port": { + "description": "The port that the node listens on for incoming quic connections.", + "value": { + "$serde_json::private::Number": "10101" + }, + "privacy": "Public" + }, + "consensus.network_config.secret_key": { + "description": "The secret key used for building the peer id. If it's an empty string a random one will be used.", + "value": "", + "privacy": "Private" + }, + "consensus.network_config.session_timeout": { + "description": "Maximal time in seconds that each session can take before failing on timeout.", + "value": { + "$serde_json::private::Number": "120" + }, + "privacy": "Public" + }, + "consensus.network_config.tcp_port": { + "description": "The port that the node listens on for incoming tcp connections.", + "value": { + "$serde_json::private::Number": "10100" + }, + "privacy": "Public" + }, + "consensus.network_topic": { + "description": "The network topic of the consensus.", + "value": "consensus", + "privacy": "Public" + }, + "consensus.num_validators": { + "description": "The number of validators in the consensus.", + "value": { + "$serde_json::private::Number": "1" + }, + "privacy": "Public" + }, + "consensus.start_height": { + "description": "The height to start the consensus from.", + "value": { + "$serde_json::private::Number": "0" + }, + "privacy": "Public" + }, + "consensus.timeouts.precommit_timeout": { + "description": "The timeout (seconds) for a precommit.", + "value": { + "$serde_json::private::Number": "1.0" + }, + "privacy": "Public" + }, + "consensus.timeouts.prevote_timeout": { + "description": "The timeout (seconds) for a prevote.", + "value": { + "$serde_json::private::Number": "1.0" + }, + "privacy": "Public" + }, + "consensus.timeouts.proposal_timeout": { + "description": "The timeout (seconds) for a proposal.", + "value": { + "$serde_json::private::Number": "3.0" + }, + "privacy": "Public" + }, + "consensus.validator_id": { + "description": "The validator id of the node.", + "value": "0x0", + "privacy": "Public" + }, + "monitoring_gateway.collect_metrics": { + "description": "If true, collect and return metrics in the monitoring gateway.", + "value": false, + "privacy": "Public" + }, + "monitoring_gateway.metric_labels": { + "description": "'label1:value1 label2:value2 ...' additional labels for metrics.", + "value": "", + "privacy": "Public" + }, + "monitoring_gateway.present_full_config_secret": { + "description": "A secret for presenting the full general config. If no value is provided, the system will generate one.", + "param_type": "String", + "privacy": "Private" + }, + "monitoring_gateway.server_address": { + "description": "node's monitoring server.", + "value": "0.0.0.0:8081", + "privacy": "Public" + }, + "monitoring_gateway.starknet_url": { + "description": "The URL of a centralized Starknet gateway.", + "value": "https://alpha-mainnet.starknet.io/", + "privacy": "Public" + }, + "network.#is_none": { + "description": "Flag for an optional field.", + "value": true, + "privacy": "TemporaryValue" + }, + "network.advertised_multiaddr": { + "description": "The external address other peers see this node. If this is set, the node will not try to find out which addresses it has and will write this address as external instead", + "value": "", + "privacy": "Public" + }, + "network.advertised_multiaddr.#is_none": { + "description": "Flag for an optional field.", + "value": true, + "privacy": "TemporaryValue" + }, + "network.bootstrap_peer_multiaddr": { + "description": "The multiaddress of the peer node. It should include the peer's id. For more info: https://docs.libp2p.io/concepts/fundamentals/peers/", + "value": "", + "privacy": "Public" + }, + "network.bootstrap_peer_multiaddr.#is_none": { + "description": "Flag for an optional field.", + "value": true, + "privacy": "TemporaryValue" + }, + "network.chain_id": { + "description": "The chain to follow. For more details see https://docs.starknet.io/documentation/architecture_and_concepts/Blocks/transactions/#chain-id.", + "value": "SN_MAIN", + "privacy": "Public" + }, + "network.discovery_config.bootstrap_dial_retry_config.base_delay_millis": { + "description": "The base delay in milliseconds for the exponential backoff strategy.", + "value": { + "$serde_json::private::Number": "2" + }, + "privacy": "Public" + }, + "network.discovery_config.bootstrap_dial_retry_config.factor": { + "description": "The factor for the exponential backoff strategy.", + "value": { + "$serde_json::private::Number": "5" + }, + "privacy": "Public" + }, + "network.discovery_config.bootstrap_dial_retry_config.max_delay_seconds": { + "description": "The maximum delay in seconds for the exponential backoff strategy.", + "value": { + "$serde_json::private::Number": "5" + }, + "privacy": "Public" + }, + "network.discovery_config.heartbeat_interval": { + "description": "The interval between each discovery (Kademlia) query in milliseconds.", + "value": { + "$serde_json::private::Number": "100" + }, + "privacy": "Public" + }, + "network.idle_connection_timeout": { + "description": "Amount of time in seconds that a connection with no active sessions will stay alive.", + "value": { + "$serde_json::private::Number": "120" + }, + "privacy": "Public" + }, + "network.peer_manager_config.malicious_timeout_seconds": { + "description": "The duration in seconds a peer is blacklisted after being marked as malicious.", + "value": { + "$serde_json::private::Number": "31536000" + }, + "privacy": "Public" + }, + "network.peer_manager_config.unstable_timeout_millis": { + "description": "The duration in milliseconds a peer blacklisted after being reported as unstable.", + "value": { + "$serde_json::private::Number": "1000" + }, + "privacy": "Public" + }, + "network.quic_port": { + "description": "The port that the node listens on for incoming quic connections.", + "value": { + "$serde_json::private::Number": "10001" + }, + "privacy": "Public" + }, + "network.secret_key": { + "description": "The secret key used for building the peer id. If it's an empty string a random one will be used.", + "value": "", + "privacy": "Private" + }, + "network.session_timeout": { + "description": "Maximal time in seconds that each session can take before failing on timeout.", + "value": { + "$serde_json::private::Number": "120" + }, + "privacy": "Public" + }, + "network.tcp_port": { + "description": "The port that the node listens on for incoming tcp connections.", + "value": { + "$serde_json::private::Number": "10000" + }, + "privacy": "Public" + }, + "p2p_sync.#is_none": { + "description": "Flag for an optional field.", + "value": true, + "privacy": "TemporaryValue" + }, + "p2p_sync.buffer_size": { + "description": "Size of the buffer for read from the storage and for incoming responses.", + "value": { + "$serde_json::private::Number": "100000" + }, + "privacy": "Public" + }, + "p2p_sync.num_block_classes_per_query": { + "description": "The maximum amount of block's classes to ask from peers in each iteration.", + "value": { + "$serde_json::private::Number": "100" + }, + "privacy": "Public" + }, + "p2p_sync.num_block_state_diffs_per_query": { + "description": "The maximum amount of block's state diffs to ask from peers in each iteration.", + "value": { + "$serde_json::private::Number": "100" + }, + "privacy": "Public" + }, + "p2p_sync.num_block_transactions_per_query": { + "description": "The maximum amount of blocks to ask their transactions from peers in each iteration.", + "value": { + "$serde_json::private::Number": "100" + }, + "privacy": "Public" + }, + "p2p_sync.num_headers_per_query": { + "description": "The maximum amount of headers to ask from peers in each iteration.", + "value": { + "$serde_json::private::Number": "10000" + }, + "privacy": "Public" + }, + "p2p_sync.stop_sync_at_block_number": { + "description": "Stops the sync at given block number and closes the node cleanly. Used to run profiling on the node.", + "value": { + "$serde_json::private::Number": "1000" + }, + "privacy": "Public" + }, + "p2p_sync.stop_sync_at_block_number.#is_none": { + "description": "Flag for an optional field.", + "value": true, + "privacy": "TemporaryValue" + }, + "p2p_sync.wait_period_for_new_data": { + "description": "Time in seconds to wait when a query returned with partial data before sending a new query", + "value": { + "$serde_json::private::Number": "5" + }, + "privacy": "Public" + }, + "rpc.chain_id": { + "description": "The chain to follow. For more details see https://docs.starknet.io/documentation/architecture_and_concepts/Blocks/transactions/#chain-id.", + "value": "SN_MAIN", + "privacy": "Public" + }, + "rpc.collect_metrics": { + "description": "If true, collect metrics for the rpc.", + "value": false, + "privacy": "Public" + }, + "rpc.execution_config.default_initial_gas_cost": { + "description": "The initial gas cost for a transaction", + "value": { + "$serde_json::private::Number": "10000000000" + }, + "privacy": "Public" + }, + "rpc.execution_config.eth_fee_contract_address": { + "description": "The eth fee token address to receive fees", + "value": "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", + "privacy": "Public" + }, + "rpc.execution_config.strk_fee_contract_address": { + "description": "The strk fee token address to receive fees", + "value": "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "privacy": "Public" + }, + "rpc.max_events_chunk_size": { + "description": "Maximum chunk size supported by the node in get_events requests.", + "value": { + "$serde_json::private::Number": "1000" + }, + "privacy": "Public" + }, + "rpc.max_events_keys": { + "description": "Maximum number of keys supported by the node in get_events requests.", + "value": { + "$serde_json::private::Number": "100" + }, + "privacy": "Public" + }, + "rpc.server_address": { + "description": "IP:PORT of the node`s JSON-RPC server.", + "value": "0.0.0.0:8080", + "privacy": "Public" + }, + "rpc.starknet_gateway_retry_config.max_retries": { + "description": "For communicating with Starknet gateway, maximum number of retries before the node stops retrying.", + "value": { + "$serde_json::private::Number": "5" + }, + "privacy": "Public" + }, + "rpc.starknet_gateway_retry_config.retry_base_millis": { + "description": "For communicating with Starknet gateway, base waiting time after a failed request. After that, the time increases exponentially.", + "value": { + "$serde_json::private::Number": "50" + }, + "privacy": "Public" + }, + "rpc.starknet_gateway_retry_config.retry_max_delay_millis": { + "description": "For communicating with Starknet gateway, max waiting time after a failed request.", + "value": { + "$serde_json::private::Number": "1000" + }, + "privacy": "Public" + }, + "rpc.starknet_url": { + "description": "URL for communicating with Starknet in write_api methods.", + "value": "https://alpha-mainnet.starknet.io/", + "privacy": "Public" + }, + "storage.db_config.chain_id": { + "description": "The chain to follow. For more details see https://docs.starknet.io/documentation/architecture_and_concepts/Blocks/transactions/#chain-id.", + "value": "SN_MAIN", + "privacy": "Public" + }, + "storage.db_config.enforce_file_exists": { + "description": "Whether to enforce that the path exists. If true, `open_env` fails when the mdbx.dat file does not exist.", + "value": false, + "privacy": "Public" + }, + "storage.db_config.growth_step": { + "description": "The growth step in bytes, must be greater than zero to allow the database to grow.", + "value": { + "$serde_json::private::Number": "4294967296" + }, + "privacy": "Public" + }, + "storage.db_config.max_size": { + "description": "The maximum size of the node's storage in bytes.", + "value": { + "$serde_json::private::Number": "1099511627776" + }, + "privacy": "Public" + }, + "storage.db_config.min_size": { + "description": "The minimum size of the node's storage in bytes.", + "value": { + "$serde_json::private::Number": "1048576" + }, + "privacy": "Public" + }, + "storage.db_config.path_prefix": { + "description": "Prefix of the path of the node's storage directory, the storage file path will be /. The path is not created automatically.", + "value": "./data", + "privacy": "Public" + }, + "storage.mmap_file_config.growth_step": { + "description": "The growth step in bytes, must be greater than max_object_size.", + "value": { + "$serde_json::private::Number": "1073741824" + }, + "privacy": "Public" + }, + "storage.mmap_file_config.max_object_size": { + "description": "The maximum size of a single object in the file in bytes", + "value": { + "$serde_json::private::Number": "268435456" + }, + "privacy": "Public" + }, + "storage.mmap_file_config.max_size": { + "description": "The maximum size of a memory mapped file in bytes. Must be greater than growth_step.", + "value": { + "$serde_json::private::Number": "1099511627776" + }, + "privacy": "Public" + }, + "storage.scope": { + "description": "The categories of data saved in storage.", + "value": "FullArchive", + "privacy": "Public" + }, + "sync.#is_none": { + "description": "Flag for an optional field.", + "value": false, + "privacy": "TemporaryValue" + }, + "sync.base_layer_propagation_sleep_duration": { + "description": "Time in seconds to poll the base layer to get the latest proved block.", + "value": { + "$serde_json::private::Number": "10" + }, + "privacy": "Public" + }, + "sync.block_propagation_sleep_duration": { + "description": "Time in seconds before checking for a new block after the node is synchronized.", + "value": { + "$serde_json::private::Number": "2" + }, + "privacy": "Public" + }, + "sync.blocks_max_stream_size": { + "description": "Max amount of blocks to download in a stream.", + "value": { + "$serde_json::private::Number": "1000" + }, + "privacy": "Public" + }, + "sync.collect_pending_data": { + "description": "Whether to collect data on pending blocks.", + "value": false, + "privacy": "Public" + }, + "sync.recoverable_error_sleep_duration": { + "description": "Waiting time in seconds before restarting synchronization after a recoverable error.", + "value": { + "$serde_json::private::Number": "3" + }, + "privacy": "Public" + }, + "sync.state_updates_max_stream_size": { + "description": "Max amount of state updates to download in a stream.", + "value": { + "$serde_json::private::Number": "1000" + }, + "privacy": "Public" + }, + "sync.verify_blocks": { + "description": "Whether to verify incoming blocks.", + "value": true, + "privacy": "Public" + } +} diff --git a/crates/sequencing/papyrus_consensus_orchestrator/src/sequencer_consensus_context.rs b/crates/sequencing/papyrus_consensus_orchestrator/src/sequencer_consensus_context.rs index b663f3d8a4..e3eac8c46a 100644 --- a/crates/sequencing/papyrus_consensus_orchestrator/src/sequencer_consensus_context.rs +++ b/crates/sequencing/papyrus_consensus_orchestrator/src/sequencer_consensus_context.rs @@ -29,6 +29,7 @@ use papyrus_protobuf::consensus::{ Vote, }; use starknet_api::block::{BlockHash, BlockHashAndNumber, BlockInfo, BlockNumber, BlockTimestamp}; +use starknet_api::core::ChainId; use starknet_api::executable_transaction::Transaction as ExecutableTransaction; use starknet_api::transaction::Transaction; use starknet_batcher_types::batcher_types::{ @@ -82,6 +83,7 @@ pub struct SequencerConsensusContext { queued_proposals: BTreeMap)>, outbound_proposal_sender: mpsc::Sender<(u64, mpsc::Receiver)>, + chain_id: ChainId, } impl SequencerConsensusContext { @@ -90,6 +92,7 @@ impl SequencerConsensusContext { _proposal_streaming_client: BroadcastTopicClient, outbound_proposal_sender: mpsc::Sender<(u64, mpsc::Receiver)>, num_validators: u64, + chain_id: ChainId, ) -> Self { Self { batcher, @@ -102,6 +105,7 @@ impl SequencerConsensusContext { current_round: 0, active_proposal: None, queued_proposals: BTreeMap::new(), + chain_id, } } } @@ -355,7 +359,8 @@ impl SequencerConsensusContext { let notify = Arc::new(Notify::new()); let notify_clone = Arc::clone(¬ify); - let handle = tokio::spawn( + let handle = tokio::spawn({ + let chain_id = self.chain_id.clone(); async move { let validate_fut = stream_validate_proposal( height, @@ -364,6 +369,7 @@ impl SequencerConsensusContext { valid_proposals, content_receiver, fin_sender, + chain_id, ); tokio::select! { _ = notify_clone.notified() => {} @@ -374,8 +380,8 @@ impl SequencerConsensusContext { } } } - .instrument(debug_span!("consensus_validate_proposal")), - ); + .instrument(debug_span!("consensus_validate_proposal")) + }); self.active_proposal = Some((notify, handle)); } @@ -477,6 +483,7 @@ async fn stream_validate_proposal( valid_proposals: Arc>, mut content_receiver: mpsc::Receiver, fin_sender: oneshot::Sender<(ProposalContentId, ProposalFin)>, + chain_id: ChainId, ) { let mut content = Vec::new(); let network_block_id = loop { @@ -485,11 +492,14 @@ async fn stream_validate_proposal( return; }; match prop_part { - ProposalPart::Transactions(TransactionBatch { transactions: txs, tx_hashes }) => { + ProposalPart::Transactions(TransactionBatch { transactions: txs, tx_hashes: _ }) => { let exe_txs: Vec = txs .into_iter() - .zip(tx_hashes.into_iter()) - .map(|tx_tup| tx_tup.into()) + .map(|tx| { + (tx, &chain_id) + .try_into() + .expect("Failed to convert transaction to executable_transation.") + }) .collect(); content.extend_from_slice(&exe_txs[..]); let input = SendProposalContentInput { diff --git a/crates/sequencing/papyrus_consensus_orchestrator/src/sequencer_consensus_context_test.rs b/crates/sequencing/papyrus_consensus_orchestrator/src/sequencer_consensus_context_test.rs index b6c1b0788f..73da389f69 100644 --- a/crates/sequencing/papyrus_consensus_orchestrator/src/sequencer_consensus_context_test.rs +++ b/crates/sequencing/papyrus_consensus_orchestrator/src/sequencer_consensus_context_test.rs @@ -21,13 +21,11 @@ use papyrus_protobuf::consensus::{ TransactionBatch, }; use starknet_api::block::{BlockHash, BlockNumber}; -use starknet_api::core::{ContractAddress, StateDiffCommitment}; -use starknet_api::executable_transaction::{ - AccountTransaction, - Transaction as ExecutableTransaction, -}; +use starknet_api::core::{ChainId, ContractAddress, Nonce, StateDiffCommitment}; +use starknet_api::executable_transaction::Transaction as ExecutableTransaction; +use starknet_api::felt; use starknet_api::hash::PoseidonHash; -use starknet_api::test_utils::invoke::{executable_invoke_tx, invoke_tx, InvokeTxArgs}; +use starknet_api::test_utils::invoke::{invoke_tx, InvokeTxArgs}; use starknet_api::transaction::{Transaction, TransactionHash}; use starknet_batcher_types::batcher_types::{ GetProposalContent, @@ -50,21 +48,19 @@ const TIMEOUT: Duration = Duration::from_millis(100); const CHANNEL_SIZE: usize = 5000; const NUM_VALIDATORS: u64 = 4; const STATE_DIFF_COMMITMENT: StateDiffCommitment = StateDiffCommitment(PoseidonHash(Felt::ZERO)); +const CHAIN_ID: ChainId = ChainId::Mainnet; lazy_static! { - static ref TX_BATCH: Vec = - vec![generate_executable_invoke_tx(Felt::THREE)]; + static ref TX_BATCH: Vec = vec![generate_executable_invoke_tx()]; } fn generate_invoke_tx() -> Transaction { - Transaction::Invoke(invoke_tx(InvokeTxArgs::default())) + Transaction::Invoke(invoke_tx(InvokeTxArgs { nonce: Nonce(felt!(3_u8)), ..Default::default() })) } -fn generate_executable_invoke_tx(tx_hash: Felt) -> ExecutableTransaction { - ExecutableTransaction::Account(AccountTransaction::Invoke(executable_invoke_tx(InvokeTxArgs { - tx_hash: TransactionHash(tx_hash), - ..Default::default() - }))) +fn generate_executable_invoke_tx() -> ExecutableTransaction { + let tx = generate_invoke_tx(); + (tx, &CHAIN_ID).try_into().unwrap() } fn make_streaming_channels() -> ( @@ -124,6 +120,7 @@ async fn build_proposal() { broadcast_topic_client, outbound_internal_sender, NUM_VALIDATORS, + CHAIN_ID, ); let init = ProposalInit { height: BlockNumber(0), @@ -186,6 +183,7 @@ async fn validate_proposal_success() { broadcast_topic_client, outbound_internal_sender, NUM_VALIDATORS, + CHAIN_ID, ); // Initialize the context for a specific height, starting with round 0. context.set_height_and_round(BlockNumber(0), 0).await; @@ -252,6 +250,7 @@ async fn repropose() { broadcast_topic_client, outbound_internal_sender, NUM_VALIDATORS, + CHAIN_ID, ); // Initialize the context for a specific height, starting with round 0. context.set_height_and_round(BlockNumber(0), 0).await; @@ -330,6 +329,7 @@ async fn proposals_from_different_rounds() { broadcast_topic_client, outbound_internal_sender, NUM_VALIDATORS, + CHAIN_ID, ); // Initialize the context for a specific height, starting with round 0. context.set_height_and_round(BlockNumber(0), 0).await; @@ -426,6 +426,7 @@ async fn interrupt_active_proposal() { broadcast_topic_client, outbound_internal_sender, NUM_VALIDATORS, + CHAIN_ID, ); // Initialize the context for a specific height, starting with round 0. context.set_height_and_round(BlockNumber(0), 0).await; diff --git a/crates/starknet_api/src/transaction.rs b/crates/starknet_api/src/transaction.rs index 77d9b4747f..f2f0e97607 100644 --- a/crates/starknet_api/src/transaction.rs +++ b/crates/starknet_api/src/transaction.rs @@ -135,22 +135,25 @@ impl From for Transaction { } } -impl From<(Transaction, TransactionHash)> for executable_transaction::Transaction { - fn from((tx, tx_hash): (Transaction, TransactionHash)) -> Self { +impl TryFrom<(Transaction, &ChainId)> for executable_transaction::Transaction { + type Error = StarknetApiError; + + fn try_from((tx, chain_id): (Transaction, &ChainId)) -> Result { + let tx_hash = tx.calculate_transaction_hash(chain_id)?; match tx { - Transaction::Invoke(tx) => executable_transaction::Transaction::Account( + Transaction::Invoke(tx) => Ok(executable_transaction::Transaction::Account( executable_transaction::AccountTransaction::Invoke( executable_transaction::InvokeTransaction { tx, tx_hash }, ), - ), - Transaction::L1Handler(tx) => executable_transaction::Transaction::L1Handler( + )), + Transaction::L1Handler(tx) => Ok(executable_transaction::Transaction::L1Handler( executable_transaction::L1HandlerTransaction { tx, tx_hash, // TODO (yael 1/12/2024): The paid fee should be an input from the l1_handler. paid_fee_on_l1: Fee(1), }, - ), + )), _ => { unimplemented!( "Unsupported transaction type. Only Invoke and L1Handler are currently \ diff --git a/crates/starknet_api/src/transaction_test.rs b/crates/starknet_api/src/transaction_test.rs index 5eb6e04ed2..9a1a83efd9 100644 --- a/crates/starknet_api/src/transaction_test.rs +++ b/crates/starknet_api/src/transaction_test.rs @@ -1,9 +1,15 @@ use assert_matches::assert_matches; use rstest::{fixture, rstest}; -use super::{Transaction, TransactionHash}; +use super::Transaction; use crate::block::NonzeroGasPrice; -use crate::executable_transaction::Transaction as ExecutableTransaction; +use crate::core::ChainId; +use crate::executable_transaction::{ + AccountTransaction, + InvokeTransaction, + L1HandlerTransaction, + Transaction as ExecutableTransaction, +}; use crate::execution_resources::GasAmount; use crate::test_utils::{read_json_file, TransactionTestData}; use crate::transaction::Fee; @@ -15,11 +21,13 @@ fn transactions_data() -> Vec { serde_json::from_value(read_json_file("transaction_hash.json")).unwrap() } -fn verify_transaction_conversion(tx: Transaction, tx_hash: TransactionHash) { - let executable_tx: ExecutableTransaction = (tx.clone(), tx_hash).into(); - let reconverted_tx = Transaction::from(executable_tx); +fn verify_transaction_conversion(tx: &Transaction, expected_executable_tx: ExecutableTransaction) { + let converted_executable_tx: ExecutableTransaction = + (tx.clone(), &ChainId::Mainnet).try_into().unwrap(); + let reconverted_tx = Transaction::from(converted_executable_tx.clone()); - assert_eq!(tx, reconverted_tx); + assert_eq!(converted_executable_tx, expected_executable_tx); + assert_eq!(tx, &reconverted_tx); } #[test] @@ -50,14 +58,20 @@ fn test_fee_div_ceil() { fn test_invoke_executable_transaction_conversion(mut transactions_data: Vec) { // Extract Invoke transaction data. let transaction_data = transactions_data.remove(0); - let tx = assert_matches!( - transaction_data.transaction, - Transaction::Invoke(tx) => Transaction::Invoke(tx), + let tx = transaction_data.transaction; + let invoke_tx = assert_matches!( + tx, + Transaction::Invoke(ref invoke_tx) => invoke_tx.clone(), "Transaction_hash.json is expected to have Invoke as the first transaction." ); - let tx_hash = transaction_data.transaction_hash; - verify_transaction_conversion(tx, tx_hash); + let expected_executable_tx = + ExecutableTransaction::Account(AccountTransaction::Invoke(InvokeTransaction { + tx: invoke_tx, + tx_hash: transaction_data.transaction_hash, + })); + + verify_transaction_conversion(&tx, expected_executable_tx); } #[rstest] @@ -66,12 +80,18 @@ fn test_l1_handler_executable_transaction_conversion( ) { // Extract L1 Handler transaction data. let transaction_data = transactions_data.remove(10); - let tx = assert_matches!( - transaction_data.transaction, - Transaction::L1Handler(tx) => Transaction::L1Handler(tx), + let tx = transaction_data.transaction; + let l1_handler_tx = assert_matches!( + tx, + Transaction::L1Handler(ref l1_handler_tx) => l1_handler_tx.clone(), "Transaction_hash.json is expected to have L1 handler as the 11th transaction." ); - let tx_hash = transaction_data.transaction_hash; - verify_transaction_conversion(tx, tx_hash); + let expected_executable_tx = ExecutableTransaction::L1Handler(L1HandlerTransaction { + tx: l1_handler_tx, + tx_hash: transaction_data.transaction_hash, + paid_fee_on_l1: Fee(1), + }); + + verify_transaction_conversion(&tx, expected_executable_tx); } diff --git a/crates/starknet_consensus_manager/src/consensus_manager.rs b/crates/starknet_consensus_manager/src/consensus_manager.rs index 1eb2aff5f8..2af29cbc2b 100644 --- a/crates/starknet_consensus_manager/src/consensus_manager.rs +++ b/crates/starknet_consensus_manager/src/consensus_manager.rs @@ -74,6 +74,7 @@ impl ConsensusManager { old_proposals_broadcast_channels.broadcast_topic_client.clone(), outbound_internal_sender, self.config.consensus_config.num_validators, + self.config.consensus_config.chain_id.clone(), ); let mut network_handle = tokio::task::spawn(network_manager.run());