Skip to content

Commit

Permalink
feat(mempool): implement priority queuedata structure
Browse files Browse the repository at this point in the history
  • Loading branch information
MohammadNassar1 committed Apr 9, 2024
1 parent ef8a66f commit 292d770
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 3 deletions.
11 changes: 10 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
resolver = "2"
members = ["crates/gateway", "crates/mempool_node"]
members = ["crates/gateway", "crates/mempool", "crates/mempool_node"]

[workspace.package]
version = "0.0.0"
Expand All @@ -27,7 +27,7 @@ pretty_assertions = "1.4.0"
rstest = "0.17.0"
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0"
starknet_api = { git = "https://github.com/starkware-libs/starknet-api.git", rev = "6b14aaf" }
starknet_api = { git = "https://github.com/starkware-libs/starknet-api.git", rev = "48d61bb" }
papyrus_config = "0.3.0"
thiserror = "1.0"
tokio = { version = "1", features = ["full"] }
Expand Down
16 changes: 16 additions & 0 deletions crates/mempool/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "starknet_mempool"
version.workspace = true
edition.workspace = true
repository.workspace = true
license.workspace = true

[lints]
workspace = true

[dependencies]
starknet_api.workspace = true

[dev-dependencies]
assert_matches.workspace = true
tokio.workspace = true
1 change: 1 addition & 0 deletions crates/mempool/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod priority_queue;
102 changes: 102 additions & 0 deletions crates/mempool/src/priority_queue.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#[cfg(test)]
#[path = "priority_queue_test.rs"]
pub mod priority_queue_test;

use std::{
cmp::Ordering,
collections::{BinaryHeap, HashMap},
};

use starknet_api::{
internal_transaction::InternalTransaction,
transaction::{
DeclareTransaction, DeployAccountTransaction, InvokeTransaction, Tip, TransactionHash,
},
};

fn get_tip(tx: &InternalTransaction) -> Tip {
match tx {
InternalTransaction::Declare(declare_tx) => match &declare_tx.tx {
DeclareTransaction::V3(declare_tx_v3) => declare_tx_v3.tip,
_ => panic!("Unexpected transaction version."),
},
InternalTransaction::DeployAccount(deploy_account_tx) => match &deploy_account_tx.tx {
DeployAccountTransaction::V3(tx_v3) => tx_v3.tip,
_ => panic!("Unexpected transaction version."),
},
InternalTransaction::Invoke(invoke_tx) => match &invoke_tx.tx {
InvokeTransaction::V3(tx_v3) => tx_v3.tip,
_ => panic!("Unexpected transaction version."),
},
}
}

fn get_tx_hash(tx: &InternalTransaction) -> TransactionHash {
match tx {
InternalTransaction::Declare(declare_tx) => declare_tx.tx_hash,
InternalTransaction::DeployAccount(deploy_account_tx) => deploy_account_tx.tx_hash,
InternalTransaction::Invoke(invoke_tx) => invoke_tx.tx_hash,
}
}

#[derive(Clone, Debug)]
pub struct MempoolTransaction {
pub tx: InternalTransaction,
pub tx_hash: TransactionHash,
}

impl PartialEq for MempoolTransaction {
fn eq(&self, other: &MempoolTransaction) -> bool {
get_tip(&self.tx) == get_tip(&other.tx)
}
}

impl Eq for MempoolTransaction {}

impl PartialOrd for MempoolTransaction {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl Ord for MempoolTransaction {
fn cmp(&self, other: &Self) -> Ordering {
get_tip(&self.tx).cmp(&get_tip(&other.tx))
}
}

#[derive(Clone, Default)]
pub struct PriorityQueue {
heap: BinaryHeap<MempoolTransaction>,
pub tx_hash_to_tx_map: HashMap<TransactionHash, InternalTransaction>,
}

impl PriorityQueue {
pub fn new() -> Self {
PriorityQueue {
heap: BinaryHeap::new(),
tx_hash_to_tx_map: HashMap::new(),
}
}

pub fn push(&mut self, tx: InternalTransaction) {
let tx_hash = get_tx_hash(&tx);
self.tx_hash_to_tx_map.insert(tx_hash, tx.clone());
let mempool_tx = MempoolTransaction { tx, tx_hash };

self.heap.push(mempool_tx);
}

// Removes and returns the transaction with the highest tip.
pub fn pop(&mut self) -> Option<TransactionHash> {
let mempool_tx = self.heap.pop();
match mempool_tx {
Some(mempool_tx) => {
let tx_hash = mempool_tx.tx_hash;
self.tx_hash_to_tx_map.remove(&tx_hash);
Some(tx_hash)
}
None => None,
}
}
}
62 changes: 62 additions & 0 deletions crates/mempool/src/priority_queue_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use starknet_api::hash::StarkFelt;
use starknet_api::{
data_availability::DataAvailabilityMode,
internal_transaction::{InternalInvokeTransaction, InternalTransaction},
transaction::{
InvokeTransaction, InvokeTransactionV3, ResourceBounds, ResourceBoundsMapping, Tip,
TransactionHash,
},
};

use crate::priority_queue::PriorityQueue;

pub fn create_tx_for_testing(tip: Tip, tx_hash: TransactionHash) -> InternalTransaction {
let tx = InvokeTransactionV3 {
resource_bounds: ResourceBoundsMapping::try_from(vec![
(
starknet_api::transaction::Resource::L1Gas,
ResourceBounds::default(),
),
(
starknet_api::transaction::Resource::L2Gas,
ResourceBounds::default(),
),
])
.expect("Resource bounds mapping has unexpected structure."),
signature: Default::default(),
nonce: Default::default(),
sender_address: Default::default(),
calldata: Default::default(),
nonce_data_availability_mode: DataAvailabilityMode::L1,
fee_data_availability_mode: DataAvailabilityMode::L1,
paymaster_data: Default::default(),
account_deployment_data: Default::default(),
tip,
};

InternalTransaction::Invoke(InternalInvokeTransaction {
tx: InvokeTransaction::V3(tx),
tx_hash,
only_query: false,
})
}

#[tokio::test]
async fn test_priority_queue() {
let tx_hash_50 = TransactionHash(StarkFelt::ONE);
let tx_hash_100 = TransactionHash(StarkFelt::TWO);
let tx_hash_10 = TransactionHash(StarkFelt::THREE);

let tx2 = create_tx_for_testing(Tip(50), tx_hash_50);
let tx1 = create_tx_for_testing(Tip(100), tx_hash_100);
let tx3 = create_tx_for_testing(Tip(10), tx_hash_10);

let mut pq = PriorityQueue::new();
pq.push(tx1);
pq.push(tx2);
pq.push(tx3);

assert_eq!(pq.pop().unwrap(), tx_hash_100);
assert_eq!(pq.pop().unwrap(), tx_hash_50);
assert_eq!(pq.pop().unwrap(), tx_hash_10);
}

0 comments on commit 292d770

Please sign in to comment.