Skip to content

Commit e129d1c

Browse files
committed
Implementation of a Mempool Syncer.
The syncers get initiated once the Mempool Executors are spawned. One Syncer per Executor is started in order to sync the regular and control transactions separately. First the Syncer starts to discover which transaction hashes other nodes with a mempool have and compare those with what we have locally. Then we distribute those unknown hashes among the peers we know have those transactions and retrieve the actual transactions. Lastly for every received transaction we do a full verification which should add it to our local mempool if everything checks out.
1 parent 0c4cbc6 commit e129d1c

9 files changed

Lines changed: 749 additions & 27 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mempool/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ nimiq-account = { workspace = true }
3636
nimiq-block = { workspace = true }
3737
nimiq-blockchain = { workspace = true }
3838
nimiq-blockchain-interface = { workspace = true }
39+
nimiq-consensus = { workspace = true }
3940
nimiq-database = { workspace = true }
4041
nimiq-hash = { workspace = true }
4142
nimiq-keys = { workspace = true }

mempool/src/executor.rs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ pub(crate) struct MempoolExecutor<N: Network, T: Topic + Unpin + Sync> {
4545
// Network ID, used for tx verification
4646
network_id: NetworkId,
4747

48-
// Transaction stream that is used to listen to transactions from the network
49-
txn_stream: BoxStream<'static, (Transaction, <N as Network>::PubsubId)>,
48+
// Transaction stream that is used to listen to transactions from the network and the Mempool Syncer
49+
txn_stream: BoxStream<'static, (Transaction, Option<<N as Network>::PubsubId>)>,
5050

5151
// Phantom data for the unused type T
5252
_phantom: PhantomData<T>,
@@ -58,7 +58,7 @@ impl<N: Network, T: Topic + Unpin + Sync> MempoolExecutor<N, T> {
5858
state: Arc<RwLock<MempoolState>>,
5959
filter: Arc<RwLock<MempoolFilter>>,
6060
network: Arc<N>,
61-
txn_stream: BoxStream<'static, (Transaction, <N as Network>::PubsubId)>,
61+
txn_stream: BoxStream<'static, (Transaction, Option<<N as Network>::PubsubId>)>,
6262
verification_tasks: Arc<AtomicU32>,
6363
) -> Self {
6464
Self {
@@ -114,16 +114,18 @@ impl<N: Network, T: Topic + Unpin + Sync> Future for MempoolExecutor<N, T> {
114114
)
115115
.await;
116116

117-
let acceptance = match verify_tx_ret {
118-
Ok(_) => MsgAcceptance::Accept,
119-
// Reject the message if signature verification fails or transaction is invalid
120-
// for current validation window
121-
Err(VerifyErr::InvalidTransaction(_)) => MsgAcceptance::Reject,
122-
Err(VerifyErr::AlreadyIncluded) => MsgAcceptance::Reject,
123-
Err(_) => MsgAcceptance::Ignore,
124-
};
125-
126-
network.validate_message::<T>(pubsub_id, acceptance);
117+
if let Some(pubsub_id) = pubsub_id {
118+
let acceptance = match verify_tx_ret {
119+
Ok(_) => MsgAcceptance::Accept,
120+
// Reject the message if signature verification fails or transaction is invalid
121+
// for current validation window
122+
Err(VerifyErr::InvalidTransaction(_)) => MsgAcceptance::Reject,
123+
Err(VerifyErr::AlreadyIncluded) => MsgAcceptance::Reject,
124+
Err(_) => MsgAcceptance::Ignore,
125+
};
126+
127+
network.validate_message::<T>(pubsub_id, acceptance);
128+
}
127129

128130
drop(decrement);
129131
});

mempool/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,7 @@ pub mod mempool;
2424
mod mempool_metrics;
2525
/// Mempool transaction module
2626
pub mod mempool_transactions;
27+
/// Mempool syncer module
28+
mod sync;
2729
/// Verify transaction module
2830
pub mod verify;

mempool/src/mempool.rs

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{
66
use futures::{
77
future::{AbortHandle, Abortable},
88
lock::{Mutex, MutexGuard},
9-
stream::{BoxStream, StreamExt},
9+
stream::{select, BoxStream, StreamExt},
1010
};
1111
use nimiq_account::ReservedBalance;
1212
use nimiq_block::Block;
@@ -32,6 +32,7 @@ use crate::{
3232
filter::{MempoolFilter, MempoolRules},
3333
mempool_state::{EvictionReason, MempoolState},
3434
mempool_transactions::{MempoolTransactions, TxPriority},
35+
sync::{messages::MempoolTransactionType, MempoolSyncer},
3536
verify::{verify_tx, VerifyErr},
3637
};
3738

@@ -90,7 +91,7 @@ impl Mempool {
9091
network: Arc<N>,
9192
monitor: Option<TaskMonitor>,
9293
mut handle: MutexGuard<'_, Option<AbortHandle>>,
93-
txn_stream: BoxStream<'static, (Transaction, <N as Network>::PubsubId)>,
94+
txn_stream: BoxStream<'static, (Transaction, Option<<N as Network>::PubsubId>)>,
9495
) {
9596
if handle.is_some() {
9697
// If we already have an executor running, don't do anything
@@ -144,29 +145,56 @@ impl Mempool {
144145
return;
145146
}
146147

148+
// TODO: get correct peers
149+
// TODO: only get peers that are synced with us
150+
// Sync regular transactions with the mempool of other peers
151+
let regular_transactions_syncer = MempoolSyncer::new(
152+
network.get_peers(),
153+
MempoolTransactionType::Regular,
154+
Arc::clone(&network),
155+
Arc::clone(&self.blockchain),
156+
Arc::clone(&self.state),
157+
);
158+
147159
// Subscribe to the network TX topic
148-
let txn_stream = network.subscribe::<TransactionTopic>().await.unwrap();
160+
let txn_stream = network
161+
.subscribe::<TransactionTopic>()
162+
.await
163+
.unwrap()
164+
.map(|(tx, pubsub_id)| (tx, Some(pubsub_id)))
165+
.boxed();
149166

150167
self.start_executor::<N, TransactionTopic>(
151168
Arc::clone(&network),
152169
monitor,
153170
executor_handle,
154-
txn_stream,
171+
select(regular_transactions_syncer, txn_stream).boxed(),
172+
);
173+
174+
// TODO: get correct peers
175+
// TODO: only get peers that are synced with us
176+
// Sync control transactions with the mempool of other peers
177+
let control_transactions_syncer = MempoolSyncer::new(
178+
network.get_peers(),
179+
MempoolTransactionType::Control,
180+
Arc::clone(&network),
181+
Arc::clone(&self.blockchain),
182+
Arc::clone(&self.state),
155183
);
156184

157185
// Subscribe to the control transaction topic
158186
let txn_stream = network
159187
.subscribe::<ControlTransactionTopic>()
160188
.await
161189
.unwrap()
162-
.map(|(tx, pubsub_id)| (Transaction::from(tx), pubsub_id))
190+
.map(|(tx, pubsub_id)| (Transaction::from(tx), Some(pubsub_id)))
163191
.boxed();
164192

165193
self.start_executor::<N, ControlTransactionTopic>(
166194
network,
167195
control_monitor,
168196
control_executor_handle,
169-
txn_stream,
197+
select(control_transactions_syncer, txn_stream).boxed(),
170198
);
171199
}
172200

@@ -177,7 +205,7 @@ impl Mempool {
177205
/// stream instead.
178206
pub async fn start_executor_with_txn_stream<N: Network>(
179207
&self,
180-
txn_stream: BoxStream<'static, (Transaction, <N as Network>::PubsubId)>,
208+
txn_stream: BoxStream<'static, (Transaction, Option<<N as Network>::PubsubId>)>,
181209
network: Arc<N>,
182210
) {
183211
self.start_executor::<N, TransactionTopic>(
@@ -195,7 +223,7 @@ impl Mempool {
195223
/// stream instead.
196224
pub async fn start_control_executor_with_txn_stream<N: Network>(
197225
&self,
198-
txn_stream: BoxStream<'static, (Transaction, <N as Network>::PubsubId)>,
226+
txn_stream: BoxStream<'static, (Transaction, Option<<N as Network>::PubsubId>)>,
199227
network: Arc<N>,
200228
) {
201229
self.start_executor::<N, ControlTransactionTopic>(

mempool/src/sync/messages.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use std::sync::Arc;
2+
3+
use nimiq_hash::Blake2bHash;
4+
use nimiq_network_interface::{
5+
network::Network,
6+
request::{Handle, RequestCommon, RequestMarker},
7+
};
8+
use nimiq_serde::{Deserialize, Serialize};
9+
use nimiq_transaction::Transaction;
10+
use parking_lot::RwLock;
11+
12+
use crate::mempool_state::MempoolState;
13+
14+
const MAX_REQUEST_RESPONSE_MEMPOOL_STATE: u32 = 1000;
15+
16+
#[derive(Clone, Debug, Deserialize, Serialize)]
17+
pub enum MempoolTransactionType {
18+
Control,
19+
Regular,
20+
}
21+
22+
/// Request the current transaction hashes in the mempool.
23+
#[derive(Clone, Debug, Deserialize, Serialize)]
24+
pub struct RequestMempoolHashes {
25+
pub transaction_type: MempoolTransactionType,
26+
}
27+
28+
#[derive(Clone, Debug, Deserialize, Serialize)]
29+
pub struct ResponseMempoolHashes {
30+
pub hashes: Vec<Blake2bHash>,
31+
}
32+
33+
impl RequestCommon for RequestMempoolHashes {
34+
type Kind = RequestMarker;
35+
const TYPE_ID: u16 = 219;
36+
type Response = ResponseMempoolHashes;
37+
const MAX_REQUESTS: u32 = MAX_REQUEST_RESPONSE_MEMPOOL_STATE;
38+
}
39+
40+
/// Request transactions in the mempool based on the provided hashes.
41+
#[derive(Clone, Debug, Deserialize, Serialize)]
42+
pub struct RequestMempoolTransactions {
43+
pub hashes: Vec<Blake2bHash>,
44+
}
45+
46+
#[derive(Clone, Debug, Deserialize, Serialize)]
47+
pub struct ResponseMempoolTransactions {
48+
pub transactions: Vec<Transaction>,
49+
}
50+
51+
impl RequestCommon for RequestMempoolTransactions {
52+
type Kind = RequestMarker;
53+
const TYPE_ID: u16 = 220;
54+
type Response = ResponseMempoolTransactions;
55+
const MAX_REQUESTS: u32 = MAX_REQUEST_RESPONSE_MEMPOOL_STATE;
56+
}
57+
58+
impl<N: Network> Handle<N, Arc<RwLock<MempoolState>>> for RequestMempoolHashes {
59+
fn handle(&self, _: N::PeerId, context: &Arc<RwLock<MempoolState>>) -> ResponseMempoolHashes {
60+
let hashes: Vec<Blake2bHash> = match self.transaction_type {
61+
MempoolTransactionType::Regular => context
62+
.read()
63+
.regular_transactions
64+
.best_transactions
65+
.iter()
66+
.map(|txn| txn.0.clone())
67+
.collect(),
68+
MempoolTransactionType::Control => context
69+
.read()
70+
.control_transactions
71+
.best_transactions
72+
.iter()
73+
.map(|txn| txn.0.clone())
74+
.collect(),
75+
};
76+
77+
ResponseMempoolHashes { hashes }
78+
}
79+
}
80+
81+
impl<N: Network> Handle<N, Arc<RwLock<MempoolState>>> for RequestMempoolTransactions {
82+
fn handle(&self, _: N::PeerId, _: &Arc<RwLock<MempoolState>>) -> ResponseMempoolTransactions {
83+
todo!()
84+
}
85+
}

0 commit comments

Comments
 (0)