Skip to content

Commit 6ec6702

Browse files
authored
Dandelion++ Rewrite (mimblewimble#2628)
* reworked the dandelion rewrite (dandelion++) * fallback to fluff/broadcast if we cannot stem the tx for any reason * rework stem vs fluff logic during accepting tx * cleanup docs * add is_stem to logging * cleanup * rustfmt * cleanup monitor and logging * rework dandelion monitor to use simple cutoff for aggregation * transition to next epoch *after* processing tx so we fluff final outstanding txs * fluff all txs in stempool if any are older than 30s aggressively aggregate when we can * fix rebase onto 1.1.0 * default config comments for Dandelion * fix code to reflect our tests - fallback to txpool on stempool error * log fluff and expire errors in dandelion monitor * cleanup * fix off by one * cleanup * cleanup * various fixes * one less clone * cleanup
1 parent ea8a5e6 commit 6ec6702

12 files changed

Lines changed: 333 additions & 372 deletions

File tree

config/src/comments.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,28 +141,29 @@ fn comments() -> HashMap<String, String> {
141141
);
142142

143143
retval.insert(
144-
"relay_secs".to_string(),
144+
"epoch_secs".to_string(),
145145
"
146-
#dandelion relay time (choose new relay peer every n secs)
146+
#dandelion epoch duration
147147
"
148148
.to_string(),
149149
);
150150

151151
retval.insert(
152-
"embargo_secs".to_string(),
152+
"aggregation_secs".to_string(),
153153
"
154-
#fluff and broadcast after embargo expires if tx not seen on network
154+
#dandelion aggregation period in secs
155155
"
156156
.to_string(),
157157
);
158158

159159
retval.insert(
160-
"patience_secs".to_string(),
160+
"embargo_secs".to_string(),
161161
"
162-
#run dandelion stem/fluff processing every n secs (stem tx aggregation in this window)
162+
#fluff and broadcast after embargo expires if tx not seen on network
163163
"
164164
.to_string(),
165165
);
166+
166167
retval.insert(
167168
"stem_probability".to_string(),
168169
"

p2p/src/peer.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
use crate::util::{Mutex, RwLock};
16+
use std::fmt;
1617
use std::fs::File;
1718
use std::net::{Shutdown, TcpStream};
1819
use std::sync::Arc;
@@ -54,6 +55,12 @@ pub struct Peer {
5455
connection: Option<Mutex<conn::Tracker>>,
5556
}
5657

58+
impl fmt::Debug for Peer {
59+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60+
write!(f, "Peer({:?})", &self.info)
61+
}
62+
}
63+
5764
impl Peer {
5865
// Only accept and connect can be externally used to build a peer
5966
fn new(info: PeerInfo, adapter: Arc<dyn NetAdapter>) -> Peer {

p2p/src/peers.rs

Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ pub struct Peers {
3737
pub adapter: Arc<dyn ChainAdapter>,
3838
store: PeerStore,
3939
peers: RwLock<HashMap<PeerAddr, Arc<Peer>>>,
40-
dandelion_relay: RwLock<Option<(i64, Arc<Peer>)>>,
4140
config: P2PConfig,
4241
}
4342

@@ -48,7 +47,6 @@ impl Peers {
4847
store,
4948
config,
5049
peers: RwLock::new(HashMap::new()),
51-
dandelion_relay: RwLock::new(None),
5250
}
5351
}
5452

@@ -87,39 +85,6 @@ impl Peers {
8785
self.save_peer(&peer_data)
8886
}
8987

90-
// Update the dandelion relay
91-
pub fn update_dandelion_relay(&self) {
92-
let peers = self.outgoing_connected_peers();
93-
94-
let peer = &self
95-
.config
96-
.dandelion_peer
97-
.and_then(|ip| peers.iter().find(|x| x.info.addr == ip))
98-
.or(thread_rng().choose(&peers));
99-
100-
match peer {
101-
Some(peer) => self.set_dandelion_relay(peer),
102-
None => debug!("Could not update dandelion relay"),
103-
}
104-
}
105-
106-
fn set_dandelion_relay(&self, peer: &Arc<Peer>) {
107-
// Clear the map and add new relay
108-
let dandelion_relay = &self.dandelion_relay;
109-
dandelion_relay
110-
.write()
111-
.replace((Utc::now().timestamp(), peer.clone()));
112-
debug!(
113-
"Successfully updated Dandelion relay to: {}",
114-
peer.info.addr
115-
);
116-
}
117-
118-
// Get the dandelion relay
119-
pub fn get_dandelion_relay(&self) -> Option<(i64, Arc<Peer>)> {
120-
self.dandelion_relay.read().clone()
121-
}
122-
12388
pub fn is_known(&self, addr: PeerAddr) -> bool {
12489
self.peers.read().contains_key(&addr)
12590
}
@@ -335,26 +300,6 @@ impl Peers {
335300
);
336301
}
337302

338-
/// Relays the provided stem transaction to our single stem peer.
339-
pub fn relay_stem_transaction(&self, tx: &core::Transaction) -> Result<(), Error> {
340-
self.get_dandelion_relay()
341-
.or_else(|| {
342-
debug!("No dandelion relay, updating.");
343-
self.update_dandelion_relay();
344-
self.get_dandelion_relay()
345-
})
346-
// If still return an error, let the caller handle this as they see fit.
347-
// The caller will "fluff" at this point as the stem phase is finished.
348-
.ok_or(Error::NoDandelionRelay)
349-
.map(|(_, relay)| {
350-
if relay.is_connected() {
351-
if let Err(e) = relay.send_stem_transaction(tx) {
352-
debug!("Error sending stem transaction to peer relay: {:?}", e);
353-
}
354-
}
355-
})
356-
}
357-
358303
/// Broadcasts the provided transaction to PEER_PREFERRED_COUNT of our
359304
/// peers. We may be connected to PEER_MAX_COUNT peers so we only
360305
/// want to broadcast to a random subset of peers.

pool/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ mod pool;
3434
pub mod transaction_pool;
3535
pub mod types;
3636

37+
pub use crate::pool::Pool;
3738
pub use crate::transaction_pool::TransactionPool;
3839
pub use crate::types::{
39-
BlockChain, DandelionConfig, PoolAdapter, PoolConfig, PoolEntryState, PoolError, TxSource,
40+
BlockChain, DandelionConfig, PoolAdapter, PoolConfig, PoolEntry, PoolError, TxSource,
4041
};

pool/src/pool.rs

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use self::core::core::{
2323
Block, BlockHeader, BlockSums, Committed, Transaction, TxKernel, Weighting,
2424
};
2525
use self::util::RwLock;
26-
use crate::types::{BlockChain, PoolEntry, PoolEntryState, PoolError};
26+
use crate::types::{BlockChain, PoolEntry, PoolError};
2727
use grin_core as core;
2828
use grin_util as util;
2929
use std::collections::{HashMap, HashSet};
@@ -139,7 +139,7 @@ impl Pool {
139139
// Verify these txs produce an aggregated tx below max tx weight.
140140
// Return a vec of all the valid txs.
141141
let txs = self.validate_raw_txs(
142-
tx_buckets,
142+
&tx_buckets,
143143
None,
144144
&header,
145145
Weighting::AsLimitedTransaction { max_weight },
@@ -167,33 +167,6 @@ impl Pool {
167167
Ok(Some(tx))
168168
}
169169

170-
pub fn select_valid_transactions(
171-
&self,
172-
txs: Vec<Transaction>,
173-
extra_tx: Option<Transaction>,
174-
header: &BlockHeader,
175-
) -> Result<Vec<Transaction>, PoolError> {
176-
let valid_txs = self.validate_raw_txs(txs, extra_tx, header, Weighting::NoLimit)?;
177-
Ok(valid_txs)
178-
}
179-
180-
pub fn get_transactions_in_state(&self, state: PoolEntryState) -> Vec<Transaction> {
181-
self.entries
182-
.iter()
183-
.filter(|x| x.state == state)
184-
.map(|x| x.tx.clone())
185-
.collect::<Vec<_>>()
186-
}
187-
188-
// Transition the specified pool entries to the new state.
189-
pub fn transition_to_state(&mut self, txs: &[Transaction], state: PoolEntryState) {
190-
for x in &mut self.entries {
191-
if txs.contains(&x.tx) {
192-
x.state = state;
193-
}
194-
}
195-
}
196-
197170
// Aggregate this new tx with all existing txs in the pool.
198171
// If we can validate the aggregated tx against the current chain state
199172
// then we can safely add the tx to the pool.
@@ -267,9 +240,9 @@ impl Pool {
267240
Ok(new_sums)
268241
}
269242

270-
fn validate_raw_txs(
243+
pub fn validate_raw_txs(
271244
&self,
272-
txs: Vec<Transaction>,
245+
txs: &[Transaction],
273246
extra_tx: Option<Transaction>,
274247
header: &BlockHeader,
275248
weighting: Weighting,
@@ -289,7 +262,7 @@ impl Pool {
289262

290263
// We know the tx is valid if the entire aggregate tx is valid.
291264
if self.validate_raw_tx(&agg_tx, header, weighting).is_ok() {
292-
valid_txs.push(tx);
265+
valid_txs.push(tx.clone());
293266
}
294267
}
295268

pool/src/transaction_pool.rs

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@ use self::core::core::verifier_cache::VerifierCache;
2323
use self::core::core::{transaction, Block, BlockHeader, Transaction, Weighting};
2424
use self::util::RwLock;
2525
use crate::pool::Pool;
26-
use crate::types::{
27-
BlockChain, PoolAdapter, PoolConfig, PoolEntry, PoolEntryState, PoolError, TxSource,
28-
};
26+
use crate::types::{BlockChain, PoolAdapter, PoolConfig, PoolEntry, PoolError, TxSource};
2927
use chrono::prelude::*;
3028
use grin_core as core;
3129
use grin_util as util;
@@ -76,13 +74,10 @@ impl TransactionPool {
7674
self.blockchain.chain_head()
7775
}
7876

77+
// Add tx to stempool (passing in all txs from txpool to validate against).
7978
fn add_to_stempool(&mut self, entry: PoolEntry, header: &BlockHeader) -> Result<(), PoolError> {
80-
// Add tx to stempool (passing in all txs from txpool to validate against).
8179
self.stempool
8280
.add_to_pool(entry, self.txpool.all_transactions(), header)?;
83-
84-
// Note: we do not notify the adapter here,
85-
// we let the dandelion monitor handle this.
8681
Ok(())
8782
}
8883

@@ -124,8 +119,6 @@ impl TransactionPool {
124119
let txpool_tx = self.txpool.all_transactions_aggregate()?;
125120
self.stempool.reconcile(txpool_tx, header)?;
126121
}
127-
128-
self.adapter.tx_accepted(&entry.tx);
129122
Ok(())
130123
}
131124

@@ -159,28 +152,25 @@ impl TransactionPool {
159152
self.blockchain.verify_coinbase_maturity(&tx)?;
160153

161154
let entry = PoolEntry {
162-
state: PoolEntryState::Fresh,
163155
src,
164156
tx_at: Utc::now(),
165157
tx,
166158
};
167159

168-
// If we are in "stem" mode then check if this is a new tx or if we have seen it before.
169-
// If new tx - add it to our stempool.
170-
// If we have seen any of the kernels before then fallback to fluff,
171-
// adding directly to txpool.
172-
if stem
173-
&& self
174-
.stempool
175-
.find_matching_transactions(entry.tx.kernels())
176-
.is_empty()
160+
// If not stem then we are fluff.
161+
// If this is a stem tx then attempt to stem.
162+
// Any problems during stem, fallback to fluff.
163+
if !stem
164+
|| self
165+
.add_to_stempool(entry.clone(), header)
166+
.and_then(|_| self.adapter.stem_tx_accepted(&entry.tx))
167+
.is_err()
177168
{
178-
self.add_to_stempool(entry, header)?;
179-
return Ok(());
169+
self.add_to_txpool(entry.clone(), header)?;
170+
self.add_to_reorg_cache(entry.clone());
171+
self.adapter.tx_accepted(&entry.tx);
180172
}
181173

182-
self.add_to_txpool(entry.clone(), header)?;
183-
self.add_to_reorg_cache(entry);
184174
Ok(())
185175
}
186176

0 commit comments

Comments
 (0)