Skip to content

Commit 859c720

Browse files
kariyclaude
andcommitted
test(rpc): add integration tests for txpool RPC namespace
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f96ffb3 commit 859c720

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
use katana_genesis::constant::DEFAULT_STRK_FEE_TOKEN_ADDRESS;
2+
use katana_primitives::{felt, Felt};
3+
use katana_rpc_api::txpool::TxPoolApiClient;
4+
use katana_utils::TestNode;
5+
use starknet::accounts::Account;
6+
use starknet::core::types::Call;
7+
use starknet::macros::selector;
8+
9+
/// Helper: creates a test node with no_mining so transactions stay in the pool.
10+
async fn setup_no_mining_node() -> TestNode {
11+
let mut config = katana_utils::node::test_config();
12+
config.sequencing.no_mining = true;
13+
TestNode::new_with_config(config).await
14+
}
15+
16+
/// Helper: submits a simple ERC-20 transfer invoke transaction.
17+
/// Returns the transaction hash.
18+
async fn send_transfer(
19+
account: &starknet::accounts::SingleOwnerAccount<
20+
starknet::providers::JsonRpcClient<starknet::providers::jsonrpc::HttpTransport>,
21+
starknet::signers::LocalWallet,
22+
>,
23+
) -> Felt {
24+
let to = DEFAULT_STRK_FEE_TOKEN_ADDRESS.into();
25+
let selector = selector!("transfer");
26+
let calldata = vec![felt!("0x1"), felt!("0x1"), Felt::ZERO];
27+
28+
let res = account
29+
.execute_v3(vec![Call { to, selector, calldata }])
30+
.l2_gas(100_000_000_000)
31+
.send()
32+
.await
33+
.unwrap();
34+
35+
res.transaction_hash
36+
}
37+
38+
#[tokio::test]
39+
async fn txpool_status_empty_pool() {
40+
let node = setup_no_mining_node().await;
41+
let client = node.rpc_http_client();
42+
43+
let status = client.txpool_status().await.unwrap();
44+
assert_eq!(status.pending, 0);
45+
assert_eq!(status.queued, 0);
46+
}
47+
48+
#[tokio::test]
49+
async fn txpool_status_after_submit() {
50+
let node = setup_no_mining_node().await;
51+
let client = node.rpc_http_client();
52+
let account = node.account();
53+
54+
send_transfer(&account).await;
55+
56+
let status = client.txpool_status().await.unwrap();
57+
assert_eq!(status.pending, 1);
58+
assert_eq!(status.queued, 0);
59+
}
60+
61+
#[tokio::test]
62+
async fn txpool_content_populated() {
63+
let node = setup_no_mining_node().await;
64+
let client = node.rpc_http_client();
65+
let account = node.account();
66+
67+
let tx_hash = send_transfer(&account).await;
68+
69+
let content = client.txpool_content().await.unwrap();
70+
71+
// Should have exactly one sender in pending
72+
assert_eq!(content.pending.len(), 1);
73+
assert!(content.queued.is_empty());
74+
75+
let sender_addr = account.address().into();
76+
let sender_txs = content.pending.get(&sender_addr).expect("sender should be present");
77+
assert_eq!(sender_txs.len(), 1);
78+
79+
// Verify the transaction fields
80+
let tx_entry = sender_txs.values().next().unwrap();
81+
assert_eq!(tx_entry.hash, tx_hash);
82+
assert_eq!(tx_entry.sender, sender_addr);
83+
}
84+
85+
#[tokio::test]
86+
async fn txpool_content_from_filters_by_address() {
87+
let node = setup_no_mining_node().await;
88+
let client = node.rpc_http_client();
89+
let account = node.account();
90+
91+
send_transfer(&account).await;
92+
93+
let sender_addr = account.address().into();
94+
95+
// Filter by the actual sender — should find the transaction
96+
let content = client.txpool_content_from(sender_addr).await.unwrap();
97+
assert_eq!(content.pending.len(), 1);
98+
assert!(content.pending.contains_key(&sender_addr));
99+
100+
// Filter by an unrelated address — should be empty
101+
let other_addr = felt!("0xdead").into();
102+
let content = client.txpool_content_from(other_addr).await.unwrap();
103+
assert!(content.pending.is_empty());
104+
}
105+
106+
#[tokio::test]
107+
async fn txpool_inspect_format() {
108+
let node = setup_no_mining_node().await;
109+
let client = node.rpc_http_client();
110+
let account = node.account();
111+
112+
send_transfer(&account).await;
113+
114+
let inspect = client.txpool_inspect().await.unwrap();
115+
116+
assert_eq!(inspect.pending.len(), 1);
117+
assert!(inspect.queued.is_empty());
118+
119+
let sender_addr = account.address().into();
120+
let summaries = inspect.pending.get(&sender_addr).expect("sender should be present");
121+
let summary = summaries.values().next().unwrap();
122+
123+
// The summary should contain key fields
124+
assert!(summary.contains("hash="), "summary should contain hash: {summary}");
125+
assert!(summary.contains("nonce="), "summary should contain nonce: {summary}");
126+
assert!(summary.contains("max_fee="), "summary should contain max_fee: {summary}");
127+
assert!(summary.contains("tip="), "summary should contain tip: {summary}");
128+
}
129+
130+
#[tokio::test]
131+
async fn txpool_cleared_after_block_produced() {
132+
// In instant mining mode, transactions are removed from the pool after the
133+
// block is produced via the BlockProductionTask polling loop.
134+
let node = TestNode::new().await;
135+
let client = node.rpc_http_client();
136+
let account = node.account();
137+
let provider = node.starknet_rpc_client();
138+
139+
let tx_hash = send_transfer(&account).await;
140+
141+
// Wait for the transaction to be included in a block
142+
katana_utils::TxWaiter::new(tx_hash, &provider).await.unwrap();
143+
144+
// After the block is produced the pool should be drained.
145+
// Poll briefly in case removal is slightly async.
146+
let mut attempts = 0;
147+
loop {
148+
let status = client.txpool_status().await.unwrap();
149+
if status.pending == 0 {
150+
break;
151+
}
152+
attempts += 1;
153+
assert!(attempts < 50, "pool not drained after mining (pending={})", status.pending);
154+
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
155+
}
156+
157+
let content = client.txpool_content().await.unwrap();
158+
assert!(content.pending.is_empty());
159+
}
160+
161+
#[tokio::test]
162+
async fn txpool_multiple_transactions() {
163+
let node = setup_no_mining_node().await;
164+
let client = node.rpc_http_client();
165+
let account = node.account();
166+
167+
// Submit 3 transactions
168+
for _ in 0..3 {
169+
send_transfer(&account).await;
170+
}
171+
172+
let status = client.txpool_status().await.unwrap();
173+
assert_eq!(status.pending, 3);
174+
175+
let content = client.txpool_content().await.unwrap();
176+
let sender_addr = account.address().into();
177+
let sender_txs = content.pending.get(&sender_addr).expect("sender should be present");
178+
assert_eq!(sender_txs.len(), 3);
179+
}

0 commit comments

Comments
 (0)