Skip to content

Commit

Permalink
feat: use cache for receipts and event txs (keep-starknet-strange#1427)
Browse files Browse the repository at this point in the history
  • Loading branch information
apoorvsadana authored Feb 2, 2024
1 parent 7572e31 commit 16a4e29
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 65 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/starknet-rpc-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,19 @@ jobs:
- name: Setup dev chain
run: |
./target/release/madara setup --chain=dev --from-local=configs
- name: Run rpc test
- name: Run rpc test without cache
run: |-
./target/release/madara --dev --sealing=manual &
MADARA_RUN_PID=$!
while ! echo exit | nc localhost 9944; do sleep 1; done
cd starknet-rpc-test
cargo test
kill $MADARA_RUN_PID
- name: Run rpc test with cache
run: |-
./target/release/madara --dev --sealing=manual --cache &
MADARA_RUN_PID=$!
while ! echo exit | nc localhost 9944; do sleep 1; done
cd starknet-rpc-test
cargo test
kill $MADARA_RUN_PID
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Next release

- chore: add cache usage for `getEvents` and `getTransactionReceipt`
- fix: cairo1 contracts should be identified by their sierra class hash
- fix(cli): repair broken cli for da conf
- feat(client): on `add_declare_transaction` store sierra contract classes in
Expand Down
36 changes: 27 additions & 9 deletions crates/client/rpc/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,35 @@ where
StarknetRpcApiError::InternalServerError
})?;

let tx_hash_and_events = self
.client
.runtime_api()
.get_starknet_events_and_their_associated_tx_hash(substrate_block_hash, block_extrinsics, chain_id)
.map_err(|e| {
error!(
"Failed to retrieve starknet events and their associated transaction hash. Substrate block hash: \
{substrate_block_hash}, chain ID: {chain_id:?}, error: {e}"
);
let index_and_events =
self.client.runtime_api().get_starknet_events_and_their_associated_tx_index(substrate_block_hash).map_err(
|e| {
error!(
"Failed to retrieve starknet events and their associated transaction index. Substrate block \
hash: {substrate_block_hash}, chain ID: {chain_id:?}, error: {e}"
);
StarknetRpcApiError::InternalServerError
},
)?;

let starknet_block = get_block_by_block_hash(self.client.as_ref(), substrate_block_hash).ok_or_else(|| {
error!("Failed to retrieve starknet block from substrate block hash");
StarknetRpcApiError::InternalServerError
})?;
let txn_hashes = self.get_cached_transaction_hashes(starknet_block.header().hash::<H>().into());
let block_extrinsics_len = block_extrinsics.len();
let starknet_txs =
self.client.runtime_api().extrinsic_filter(substrate_block_hash, block_extrinsics).map_err(|e| {
error!("Failed to filter extrinsics. Substrate block hash: {substrate_block_hash}, error: {e}");
StarknetRpcApiError::InternalServerError
})?;
let inherent_count = block_extrinsics_len - starknet_txs.len();
let mut tx_hash_and_events: Vec<(Felt252Wrapper, starknet_api::transaction::Event)> = vec![];
for (index, event) in index_and_events {
let tx_index = index as usize - inherent_count;
let tx_hash = self.try_txn_hash_from_cache(tx_index, &txn_hashes, &starknet_txs, chain_id)?;
tx_hash_and_events.push((tx_hash, event));
}

let starknet_block = get_block_by_block_hash(self.client.as_ref(), substrate_block_hash)
.ok_or(StarknetRpcApiError::BlockNotFound)?;
Expand Down
72 changes: 59 additions & 13 deletions crates/client/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,40 @@ where
/// * `block_hash` - The hash of the block containing the transactions (starknet block).
fn get_cached_transaction_hashes(&self, block_hash: H256) -> Option<Vec<H256>> {
self.backend.mapping().cached_transaction_hashes_from_block_hash(block_hash).unwrap_or_else(|err| {
error!("{err}");
error!("Failed to read from cache: {err}");
None
})
}

fn try_txn_hash_from_cache(
&self,
tx_index: usize,
cached_transactions: &Option<Vec<H256>>,
transactions: &[mp_transactions::Transaction],
chain_id: Felt252Wrapper,
) -> Result<Felt252Wrapper, StarknetRpcApiError> {
if let Some(txn_hashes) = &cached_transactions {
let txn_hash = (&txn_hashes
.get(tx_index)
.ok_or_else(|| {
error!("Failed to retrieve transaction hash from cache, invalid index {}", tx_index);
StarknetRpcApiError::InternalServerError
})?
.0)
.try_into()
.map_err(|_| {
error!("Failed to convert transaction hash");
StarknetRpcApiError::InternalServerError
})?;
Ok(txn_hash)
} else {
let transaction = &transactions.get(tx_index).ok_or_else(|| {
error!("Failed to retrieve transaction hash from starknet txs, invalid index {}", tx_index);
StarknetRpcApiError::InternalServerError
})?;
Ok(transaction.compute_hash::<H>(chain_id, false))
}
}
}

/// Taken from https://github.com/paritytech/substrate/blob/master/client/rpc/src/author/mod.rs#L78
Expand Down Expand Up @@ -1436,23 +1466,39 @@ where
StarknetRpcApiError::InternalServerError
})?;

let (tx_index, transaction) = self
.client
.runtime_api()
.get_index_and_tx_for_tx_hash(substrate_block_hash, block_extrinsics, chain_id, transaction_hash.into())
.map_err(|e| {
error!(
"Failed to get index for transaction hash. Substrate block hash: {substrate_block_hash}, \
transaction hash: {transaction_hash}, error: {e}"
);
let block_extrinsics_len = block_extrinsics.len();
let transactions =
self.client.runtime_api().extrinsic_filter(substrate_block_hash, block_extrinsics).map_err(|e| {
error!("Failed to filter extrinsics. Substrate block hash: {substrate_block_hash}, error: {e}");
StarknetRpcApiError::InternalServerError
})?
.expect("the transaction should be present in the substrate extrinsics"); // not reachable
})?;
let txn_hashes = self.get_cached_transaction_hashes(starknet_block.header().hash::<H>().into());
let mut tx_index = None;
let mut transaction = None;
for (index, tx) in transactions.iter().enumerate() {
let tx_hash = self.try_txn_hash_from_cache(index, &txn_hashes, &transactions, chain_id)?;
if tx_hash == transaction_hash.into() {
tx_index = Some(index);
transaction = Some(tx);
break;
}
}
if tx_index.is_none() || transaction.is_none() {
error!(
"Failed to find transaction hash in block. Substrate block hash: {substrate_block_hash}, transaction \
hash: {transaction_hash}"
);
return Err(StarknetRpcApiError::InternalServerError.into());
}
let tx_index = tx_index.unwrap();
let transaction = transaction.unwrap();
// adding the inherents count to the tx_index to get the correct index in the block
let tx_index = tx_index + block_extrinsics_len - transactions.len();

let events = self
.client
.runtime_api()
.get_events_for_tx_by_index(substrate_block_hash, tx_index)
.get_events_for_tx_by_index(substrate_block_hash, tx_index as u32)
.map_err(|e| {
error!(
"Failed to get events for transaction index. Substrate block hash: {substrate_block_hash}, \
Expand Down
5 changes: 2 additions & 3 deletions crates/pallets/starknet/runtime_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,13 @@ sp_api::decl_runtime_apis! {
/// the runtime itself, accomplished through the extrinsic_filter method. This enables the
/// client to operate seamlessly while abstracting the extrinsic complexity.
fn extrinsic_filter(xts: Vec<<Block as BlockT>::Extrinsic>) -> Vec<Transaction>;
fn get_index_and_tx_for_tx_hash(xts: Vec<<Block as BlockT>::Extrinsic>, chain_id: Felt252Wrapper, tx_hash: Felt252Wrapper) -> Option<(u32, Transaction)>;
/// Returns events, call with index from get_index_and_tx_for_tx_hash method
/// Returns events, call with extrinsic index
fn get_events_for_tx_by_index(tx_index: u32) -> Option<Vec<StarknetEvent>>;

/// Return the list of StarknetEvent evmitted during this block, along with the hash of the starknet transaction they bellong to
///
/// `block_extrinsics` is the list of all the extrinsic executed during this block, it is used in order to match
fn get_starknet_events_and_their_associated_tx_hash(block_extrinsics: Vec<<Block as BlockT>::Extrinsic>, chain_id: Felt252Wrapper) -> Vec<(Felt252Wrapper, StarknetEvent)>;
fn get_starknet_events_and_their_associated_tx_index() -> Vec<(u32, StarknetEvent)>;
/// Return the outcome of the tx execution
fn get_tx_execution_outcome(tx_hash: TransactionHash) -> Option<Vec<u8>>;
/// Return the block context
Expand Down
42 changes: 3 additions & 39 deletions crates/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,13 @@ pub use frame_system::Call as SystemCall;
use frame_system::{EventRecord, Phase};
use mp_felt::Felt252Wrapper;
use mp_simulations::{PlaceHolderErrorTypeForFailedStarknetExecution, SimulationFlags};
use mp_transactions::compute_hash::ComputeTransactionHash;
use mp_transactions::{HandleL1MessageTransaction, Transaction, UserTransaction};
use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList};
/// Import the Starknet pallet.
pub use pallet_starknet;
use pallet_starknet::pallet::Error as PalletError;
use pallet_starknet::Call::{consume_l1_message, declare, deploy_account, invoke};
use pallet_starknet::{Config, Event};
use pallet_starknet::Event;
use pallet_starknet_runtime_api::StarknetTransactionExecutionError;
pub use pallet_timestamp::Call as TimestampCall;
use sp_api::impl_runtime_apis;
Expand Down Expand Up @@ -288,28 +287,18 @@ impl_runtime_apis! {
Starknet::simulate_transactions(transactions, &simulation_flags)
}

fn get_starknet_events_and_their_associated_tx_hash(block_extrinsics: Vec<<Block as BlockT>::Extrinsic>, chain_id: Felt252Wrapper) -> Vec<(Felt252Wrapper, StarknetEvent)> {
fn get_starknet_events_and_their_associated_tx_index() -> Vec<(u32, StarknetEvent)> {
System::read_events_no_consensus().filter_map(|event_record| {
let (phase, event) = match *event_record {
EventRecord { event: RuntimeEvent::Starknet(Event::StarknetEvent(event)), phase, .. } => (phase, event),
_ => return None,
};

let index = match phase {
Phase::ApplyExtrinsic(idx) => {idx},
_ => return None

};
let extrinsic = &block_extrinsics[index as usize];
let tx_hash = match &extrinsic.function {
RuntimeCall::Starknet( invoke { transaction }) => transaction.compute_hash::<<Runtime as Config>::SystemHash>(chain_id, false),
RuntimeCall::Starknet( declare { transaction, .. }) => transaction.compute_hash::<<Runtime as Config>::SystemHash>(chain_id, false),
RuntimeCall::Starknet( deploy_account { transaction }) => transaction.compute_hash::<<Runtime as Config>::SystemHash>(chain_id, false),
RuntimeCall::Starknet( consume_l1_message { transaction, .. }) => transaction.compute_hash::<<Runtime as Config>::SystemHash>(chain_id, false),
_ => return None,
};

Some((tx_hash, event))
Some((index, event))
}).collect()
}

Expand All @@ -323,31 +312,6 @@ impl_runtime_apis! {
}).collect::<Vec<Transaction>>()
}

fn get_index_and_tx_for_tx_hash(extrinsics: Vec<<Block as BlockT>::Extrinsic>, chain_id: Felt252Wrapper, tx_hash: Felt252Wrapper) -> Option<(u32, Transaction)> {
// Find our tx and it's index
let (tx_index, tx) = extrinsics.into_iter().enumerate().find(|(_, xt)| {
let computed_tx_hash = match &xt.function {
RuntimeCall::Starknet( invoke { transaction }) => transaction.compute_hash::<<Runtime as Config>::SystemHash>(chain_id, false),
RuntimeCall::Starknet( declare { transaction, .. }) => transaction.compute_hash::<<Runtime as Config>::SystemHash>(chain_id, false),
RuntimeCall::Starknet( deploy_account { transaction }) => transaction.compute_hash::<<Runtime as Config>::SystemHash>(chain_id, false),
RuntimeCall::Starknet( consume_l1_message { transaction, .. }) => transaction.compute_hash::<<Runtime as Config>::SystemHash>(chain_id, false),
_ => return false
};

computed_tx_hash == tx_hash
})?;
let transaction = match tx.function {
RuntimeCall::Starknet( invoke { transaction }) => Transaction::Invoke(transaction),
RuntimeCall::Starknet( declare { transaction, .. }) => Transaction::Declare(transaction),
RuntimeCall::Starknet( deploy_account { transaction }) => Transaction::DeployAccount(transaction),
RuntimeCall::Starknet( consume_l1_message { transaction, .. }) => Transaction::L1Handler(transaction),
_ => unreachable!("The previous match made sure that at this point tx is one of those starknet calls"),
};

let tx_index = u32::try_from(tx_index).expect("unexpected number of transactions");
Some((tx_index, transaction))
}

fn get_events_for_tx_by_index(tx_index: u32) -> Option<Vec<StarknetEvent>> {

// Skip all the events that are not related to our tx
Expand Down

0 comments on commit 16a4e29

Please sign in to comment.