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 starknet_getMessagesStatus #621

Merged
merged 19 commits into from
Oct 11, 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
5 changes: 4 additions & 1 deletion crates/starknet-devnet-core/src/messaging/ethereum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use ethers::prelude::*;
use ethers::providers::{Http, Provider, ProviderError};
use ethers::types::{Address, BlockNumber, Log};
use k256::ecdsa::SigningKey;
use starknet_rs_core::types::Felt;
use starknet_rs_core::types::{Felt, Hash256};
use starknet_types::felt::felt_from_prefixed_hex;
use starknet_types::rpc::contract_address::ContractAddress;
use starknet_types::rpc::messaging::{MessageToL1, MessageToL2};
Expand Down Expand Up @@ -302,6 +302,7 @@ impl EthereumMessaging {
///
/// * `log` - The log to be converted.
pub fn message_to_l2_from_log(log: Log) -> DevnetResult<MessageToL2> {
let l1_transaction_hash = log.transaction_hash.map(|h| Hash256::from_bytes(h.to_fixed_bytes()));
let parsed_log = <LogMessageToL2 as EthLogDecode>::decode_log(&log.into()).map_err(|e| {
Error::MessagingError(MessagingError::EthersError(format!("Log parsing failed {}", e)))
})?;
Expand All @@ -318,6 +319,7 @@ pub fn message_to_l2_from_log(log: Log) -> DevnetResult<MessageToL2> {
}

Ok(MessageToL2 {
l1_transaction_hash,
l2_contract_address: contract_address,
entry_point_selector,
l1_contract_address: ContractAddress::new(from_address)?,
Expand Down Expand Up @@ -405,6 +407,7 @@ mod tests {
};

let expected_message = MessageToL2 {
l1_transaction_hash: None,
l1_contract_address: ContractAddress::new(
felt_from_prefixed_hex(from_address).unwrap(),
)
Expand Down
19 changes: 9 additions & 10 deletions crates/starknet-devnet-core/src/messaging/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
//! contract (`mockSendMessageFromL2` entrypoint).
use std::collections::HashMap;

use starknet_rs_core::types::{BlockId, ExecutionResult, Hash256};
use ethers::types::H256;
use starknet_rs_core::types::{BlockId, ExecutionResult, Felt, Hash256};
use starknet_types::rpc::messaging::{MessageToL1, MessageToL2};

use crate::error::{DevnetResult, Error, MessagingError};
Expand All @@ -54,13 +55,11 @@ pub struct MessagingBroker {
/// For each time a message is supposed to be sent to L1, it is stored in this
/// queue. The user may consume those messages using `consume_message_from_l2`
/// to actually test `MessageToL1` emitted without running L1 node.
///
/// Note:
/// `Hash256` is not directly supported as a HashMap key due to missing trait.
/// Using `String` instead.
pub l2_to_l1_messages_hashes: HashMap<String, u64>,
pub l2_to_l1_messages_hashes: HashMap<H256, u64>,
/// This list of messages that will be sent to L1 node at the next `postman/flush`.
pub l2_to_l1_messages_to_flush: Vec<MessageToL1>,
/// Mapping of L1 transaction hash to a chronological sequence of generated L2 transactions.
pub l1_to_l2_tx_hashes: HashMap<H256, Vec<Felt>>,
}

impl MessagingBroker {
Expand Down Expand Up @@ -142,7 +141,7 @@ impl Starknet {
}

for message in &messages {
let hash = format!("{}", message.hash());
let hash = H256(*message.hash().as_bytes());
let count = self.messaging.l2_to_l1_messages_hashes.entry(hash).or_insert(0);
*count += 1;
}
Expand Down Expand Up @@ -188,14 +187,14 @@ impl Starknet {
// Ensure latest messages are collected before consuming the message.
self.collect_messages_to_l1().await?;

let hash = format!("{}", message.hash());
let count = self.messaging.l2_to_l1_messages_hashes.entry(hash.clone()).or_insert(0);
let hash = H256(*message.hash().as_bytes());
let count = self.messaging.l2_to_l1_messages_hashes.entry(hash).or_insert(0);

if *count > 0 {
*count -= 1;
Ok(message.hash())
} else {
Err(Error::MessagingError(MessagingError::MessageToL1NotPresent(hash)))
Err(Error::MessagingError(MessagingError::MessageToL1NotPresent(hash.to_string())))
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use blockifier::transaction::transactions::ExecutableTransaction;
use ethers::types::H256;
use starknet_types::felt::TransactionHash;
use starknet_types::rpc::transactions::l1_handler_transaction::L1HandlerTransaction;
use starknet_types::rpc::transactions::{Transaction, TransactionWithHash};
Expand Down Expand Up @@ -34,6 +35,17 @@ pub fn add_l1_handler_transaction(
blockifier_execution_result,
)?;

// If L1 tx hash present, store the generated L2 tx hash in its messaging entry.
// Not done as part of `handle_transaction_result` as it is specific to this tx type.
if let Some(l1_tx_hash) = transaction.l1_transaction_hash {
starknet
.messaging
.l1_to_l2_tx_hashes
.entry(H256(*l1_tx_hash.as_bytes()))
.or_default()
.push(transaction_hash);
}

Ok(transaction_hash)
}

Expand Down
31 changes: 28 additions & 3 deletions crates/starknet-devnet-core/src/starknet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ use blockifier::transaction::account_transaction::AccountTransaction;
use blockifier::transaction::errors::TransactionPreValidationError;
use blockifier::transaction::objects::TransactionExecutionInfo;
use blockifier::transaction::transactions::ExecutableTransaction;
use ethers::types::H256;
use parking_lot::RwLock;
use starknet_api::block::{BlockNumber, BlockStatus, BlockTimestamp, GasPrice, GasPricePerToken};
use starknet_api::core::SequencerContractAddress;
use starknet_api::felt;
use starknet_api::transaction::Fee;
use starknet_config::BlockGenerationOn;
use starknet_rs_core::types::{
BlockId, BlockTag, Call, ExecutionResult, Felt, MsgFromL1, TransactionExecutionStatus,
BlockId, BlockTag, Call, ExecutionResult, Felt, Hash256, MsgFromL1, TransactionExecutionStatus,
TransactionFinalityStatus,
};
use starknet_rs_core::utils::get_selector_from_name;
Expand Down Expand Up @@ -47,8 +48,8 @@ use starknet_types::rpc::transactions::l1_handler_transaction::L1HandlerTransact
use starknet_types::rpc::transactions::{
BlockTransactionTrace, BroadcastedDeclareTransaction, BroadcastedDeployAccountTransaction,
BroadcastedInvokeTransaction, BroadcastedTransaction, BroadcastedTransactionCommon,
SimulatedTransaction, SimulationFlag, TransactionTrace, TransactionType, TransactionWithHash,
TransactionWithReceipt, Transactions,
L1HandlerTransactionStatus, SimulatedTransaction, SimulationFlag, TransactionTrace,
TransactionType, TransactionWithHash, TransactionWithReceipt, Transactions,
};
use starknet_types::traits::HashProducer;
use tracing::{error, info};
Expand Down Expand Up @@ -1380,6 +1381,30 @@ impl Starknet {
Ok(false)
}
}

pub fn get_messages_status(
&self,
l1_tx_hash: Hash256,
) -> Option<Vec<L1HandlerTransactionStatus>> {
match self.messaging.l1_to_l2_tx_hashes.get(&H256(*l1_tx_hash.as_bytes())) {
Some(l2_tx_hashes) => {
let mut statuses = vec![];
for l2_tx_hash in l2_tx_hashes {
match self.transactions.get(l2_tx_hash) {
Some(l2_tx) => statuses.push(L1HandlerTransactionStatus {
transaction_hash: *l2_tx_hash,
finality_status: l2_tx.finality_status,
failure_reason: l2_tx.execution_info.revert_error.clone(),
}),
// should never happen due to handling in add_l1_handler_transaction
None => return None,
}
}
Some(statuses)
}
None => None,
}
}
}

#[cfg(test)]
Expand Down
16 changes: 15 additions & 1 deletion crates/starknet-devnet-server/src/api/json_rpc/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use starknet_types::rpc::transactions::{
use starknet_types::starknet_api::block::BlockStatus;

use super::error::{ApiError, StrictRpcResult};
use super::models::{BlockHashAndNumberOutput, SyncingOutput, TransactionStatusOutput};
use super::models::{
BlockHashAndNumberOutput, L1TransactionHashInput, SyncingOutput, TransactionStatusOutput,
};
use super::{DevnetResponse, JsonRpcHandler, JsonRpcResponse, StarknetResponse, RPC_SPEC_VERSION};
use crate::api::http::endpoints::accounts::{
get_account_balance_impl, get_predeployed_accounts_impl, BalanceQuery, PredeployedAccountsQuery,
Expand Down Expand Up @@ -463,6 +465,18 @@ impl JsonRpcHandler {
}
}

/// starknet_getMessagesStatus
pub async fn get_messages_status(
&self,
L1TransactionHashInput { transaction_hash }: L1TransactionHashInput,
) -> StrictRpcResult {
let starknet = self.api.starknet.lock().await;
match starknet.get_messages_status(transaction_hash) {
Some(statuses) => Ok(StarknetResponse::MessagesStatusByL1Hash(statuses).into()),
None => Err(ApiError::TransactionNotFound),
}
}

/// devnet_getPredeployedAccounts
pub async fn get_predeployed_accounts(
&self,
Expand Down
10 changes: 8 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 @@ -13,7 +13,8 @@ use enum_helper_macros::{AllVariantsSerdeRenames, VariantName};
use futures::StreamExt;
use models::{
BlockAndClassHashInput, BlockAndContractAddressInput, BlockAndIndexInput, CallInput,
EstimateFeeInput, EventsInput, GetStorageInput, TransactionHashInput, TransactionHashOutput,
EstimateFeeInput, EventsInput, GetStorageInput, L1TransactionHashInput, TransactionHashInput,
TransactionHashOutput,
};
use serde::{Deserialize, Serialize};
use serde_json::json;
Expand All @@ -28,7 +29,8 @@ use starknet_types::rpc::gas_modification::{GasModification, GasModificationRequ
use starknet_types::rpc::state::{PendingStateUpdate, StateUpdate};
use starknet_types::rpc::transaction_receipt::TransactionReceipt;
use starknet_types::rpc::transactions::{
BlockTransactionTrace, EventsChunk, SimulatedTransaction, TransactionTrace, TransactionWithHash,
BlockTransactionTrace, EventsChunk, L1HandlerTransactionStatus, SimulatedTransaction,
TransactionTrace, TransactionWithHash,
};
use starknet_types::starknet_api::block::BlockNumber;
use tracing::{error, info, trace};
Expand Down Expand Up @@ -337,6 +339,7 @@ impl JsonRpcHandler {
JsonRpcRequest::AccountBalance(data) => self.get_account_balance(data).await,
JsonRpcRequest::Mint(data) => self.mint(data).await,
JsonRpcRequest::DevnetConfig => self.get_devnet_config().await,
JsonRpcRequest::MessagesStatusByL1Hash(data) => self.get_messages_status(data).await,
};

// If locally we got an error and forking is set up, forward the request to the origin
Expand Down Expand Up @@ -476,6 +479,8 @@ pub enum JsonRpcRequest {
TransactionReceiptByTransactionHash(TransactionHashInput),
#[serde(rename = "starknet_getTransactionStatus")]
TransactionStatusByHash(TransactionHashInput),
#[serde(rename = "starknet_getMessagesStatus")]
MessagesStatusByL1Hash(L1TransactionHashInput),
#[serde(rename = "starknet_getClass")]
ClassByHash(BlockAndClassHashInput),
#[serde(rename = "starknet_getClassHashAt")]
Expand Down Expand Up @@ -609,6 +614,7 @@ pub enum StarknetResponse {
SimulateTransactions(Vec<SimulatedTransaction>),
TraceTransaction(TransactionTrace),
BlockTransactionTraces(Vec<BlockTransactionTrace>),
MessagesStatusByL1Hash(Vec<L1HandlerTransactionStatus>),
}

#[derive(Serialize)]
Expand Down
8 changes: 7 additions & 1 deletion crates/starknet-devnet-server/src/api/json_rpc/models.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use starknet_rs_core::types::{TransactionExecutionStatus, TransactionFinalityStatus};
use starknet_rs_core::types::{Hash256, TransactionExecutionStatus, TransactionFinalityStatus};
use starknet_types::contract_address::ContractAddress;
use starknet_types::felt::{BlockHash, ClassHash, TransactionHash};
use starknet_types::patricia_key::PatriciaKey;
Expand Down Expand Up @@ -172,6 +172,12 @@ pub struct TransactionStatusOutput {
pub execution_status: TransactionExecutionStatus,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct L1TransactionHashInput {
pub transaction_hash: Hash256,
}

#[cfg(test)]
mod tests {
use starknet_rs_core::types::{BlockId as ImportedBlockId, BlockTag, Felt};
Expand Down
1 change: 1 addition & 0 deletions crates/starknet-devnet-types/src/rpc/messaging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::rpc::eth_address::EthAddressWrapper;
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct MessageToL2 {
pub l1_transaction_hash: Option<Hash256>,
pub l2_contract_address: ContractAddress,
pub entry_point_selector: EntryPointSelector,
pub l1_contract_address: ContractAddress,
Expand Down
8 changes: 8 additions & 0 deletions crates/starknet-devnet-types/src/rpc/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,14 @@ impl FunctionInvocation {
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct L1HandlerTransactionStatus {
pub transaction_hash: TransactionHash,
pub finality_status: TransactionFinalityStatus,
pub failure_reason: Option<String>,
}

#[cfg(test)]
mod tests {
use starknet_rs_crypto::poseidon_hash_many;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use starknet_api::transaction::{
TransactionHash as ApiTransactionHash, TransactionVersion as ApiTransactionVersion,
};
use starknet_rs_core::crypto::compute_hash_on_elements;
use starknet_rs_core::types::Felt;
use starknet_rs_core::types::{Felt, Hash256};

use super::{deserialize_paid_fee_on_l1, serialize_paid_fee_on_l1};
use crate::constants::PREFIX_L1_HANDLER;
Expand All @@ -23,6 +23,9 @@ use crate::rpc::messaging::MessageToL2;
#[derive(Debug, Clone, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct L1HandlerTransaction {
/// The hash of the L1 transaction that triggered this L1 handler execution.
/// Omissible if received via mock (devnet_postmanSendMessageToL2)
pub l1_transaction_hash: Option<Hash256>,
pub version: TransactionVersion,
pub nonce: Nonce,
pub contract_address: ContractAddress,
Expand Down Expand Up @@ -102,9 +105,8 @@ impl L1HandlerTransaction {
calldata,
nonce: message.nonce,
paid_fee_on_l1,
// Currently, only version 0 is supported, which
// is ensured by default initialization.
..Default::default()
l1_transaction_hash: message.l1_transaction_hash,
version: Felt::ZERO, // currently, only version 0 is supported
})
}
}
Expand All @@ -122,6 +124,7 @@ impl TryFrom<&L1HandlerTransaction> for MessageToL2 {

let payload = value.calldata[1..].to_vec();
Ok(MessageToL2 {
l1_transaction_hash: value.l1_transaction_hash,
l2_contract_address: value.contract_address,
entry_point_selector: value.entry_point_selector,
l1_contract_address: ContractAddress::new(*l1_contract_address)?,
Expand Down Expand Up @@ -158,6 +161,7 @@ mod tests {
vec![felt_from_prefixed_hex(from_address).unwrap(), Felt::ONE, Felt::TWO];

let message = MessageToL2 {
l1_transaction_hash: None,
l1_contract_address: ContractAddress::new(
felt_from_prefixed_hex(from_address).unwrap(),
)
Expand All @@ -170,8 +174,6 @@ mod tests {
paid_fee_on_l1: fee.into(),
};

let chain_id = ChainId::goerli_legacy_id();

let transaction_hash = felt_from_prefixed_hex(
"0x6182c63599a9638272f1ce5b5cadabece9c81c2d2b8f88ab7a294472b8fce8b",
)
Expand Down Expand Up @@ -199,6 +201,6 @@ mod tests {
let transaction = L1HandlerTransaction::try_from_message_to_l2(message).unwrap();

assert_eq!(transaction, expected_tx);
assert_eq!(transaction.compute_hash(chain_id), transaction_hash);
assert_eq!(transaction.compute_hash(ChainId::goerli_legacy_id()), transaction_hash);
}
}
Loading