Basic Dandelion transaction routing (#719)

* Initial Dandelion Commit
* Changed stem_tx_pool to tx_stempool
* Introduction of stem memory pool and stem pool config
* Pool push now send to stem memory pool
* Add stem transaction functions
* Add stem transaction pool
* Drastically simplified code structure
* Add monitor transactions
* Add Dandelion monitor and remove transactions from stempool
* Add peer relay monitor
* Reconcile block with stempool
* Fix total size bug
* Add fluff option for pool push
* Added details on dandelion monitor
* Fix issue with missing parent
* Child transaction with stempool parent are now forced stem
* Update Dandelion Relay from outgoing peers
* Fix missing pool reconciliation
* Add the ability to fluff a transaction directly
* Fix tests for Dandelion
* Missing send_stem_transaction method...
* Add fluff handler for wallet
* Add logger when successfully updated Dandelion relay
* Launch transaction monitor last
* Fix dandelion relay misplaced
* Add logging and updating for stempool
* Additionnal check for stem transaction
* Fix 2 Locks in a row
This commit is contained in:
Quentin Le Sceller
2018-03-19 22:18:54 -05:00
committed by Ignotus Peverell
parent 7816f35238
commit fcfe7bc6a4
24 changed files with 962 additions and 83 deletions
+1
View File
@@ -64,6 +64,7 @@ enum_from_primitive! {
Block,
GetCompactBlock,
CompactBlock,
StemTransaction,
Transaction,
TxHashSetRequest,
TxHashSetArchive
+22 -2
View File
@@ -235,6 +235,26 @@ impl Peer {
}
}
/// Sends the provided stem transaction to the remote peer. The request may be
/// dropped if the remote peer is known to already have the stem transaction.
pub fn send_stem_transaction(&self, tx: &core::Transaction) -> Result<(), Error> {
if !self.tracking_adapter.has(tx.hash()) {
debug!(LOGGER, "Send tx {} to {}", tx.hash(), self.info.addr);
self.connection
.as_ref()
.unwrap()
.send(tx, msg::Type::StemTransaction)
} else {
debug!(
LOGGER,
"Not sending tx {} to {} (already seen)",
tx.hash(),
self.info.addr
);
Ok(())
}
}
/// Sends a request for block headers from the provided block locator
pub fn send_header_request(&self, locator: Vec<Hash>) -> Result<(), Error> {
self.connection
@@ -356,9 +376,9 @@ impl ChainAdapter for TrackingAdapter {
self.adapter.total_height()
}
fn transaction_received(&self, tx: core::Transaction) {
fn transaction_received(&self, tx: core::Transaction, stem: bool) {
self.push(tx.hash());
self.adapter.transaction_received(tx)
self.adapter.transaction_received(tx, stem)
}
fn block_received(&self, b: core::Block, addr: SocketAddr) -> bool {
+69 -2
View File
@@ -33,6 +33,7 @@ pub struct Peers {
pub adapter: Arc<ChainAdapter>,
store: PeerStore,
peers: RwLock<HashMap<SocketAddr, Arc<RwLock<Peer>>>>,
dandelion_relay: RwLock<HashMap<i64, Arc<RwLock<Peer>>>>,
}
unsafe impl Send for Peers {}
@@ -44,6 +45,7 @@ impl Peers {
adapter,
store,
peers: RwLock::new(HashMap::new()),
dandelion_relay: RwLock::new(HashMap::new()),
}
}
@@ -71,6 +73,34 @@ impl Peers {
apeer.clone()
}
// Update the dandelion relay
pub fn update_dandelion_relay(&self) {
let peers = self.outgoing_connected_peers();
match thread_rng().choose(&peers) {
Some(peer) => {
// Clear the map and add new relay
let dandelion_relay = &self.dandelion_relay;
dandelion_relay.write().unwrap().clear();
dandelion_relay
.write()
.unwrap()
.insert(time::now_utc().to_timespec().sec, peer.clone());
debug!(
LOGGER,
"Successfully updated Dandelion relay to: {}",
peer.try_read().unwrap().info.addr
);
}
None => error!(LOGGER, "Could not update dandelion relay"),
};
}
// Get the dandelion relay
pub fn get_dandelion_relay(&self) -> HashMap<i64, Arc<RwLock<Peer>>> {
let res = self.dandelion_relay.read().unwrap().clone();
res
}
pub fn is_known(&self, addr: &SocketAddr) -> bool {
self.get_connected_peer(addr).is_some()
}
@@ -87,6 +117,19 @@ impl Peers {
res
}
pub fn outgoing_connected_peers(&self) -> Vec<Arc<RwLock<Peer>>> {
let peers = self.connected_peers();
let res = peers
.iter()
.filter(|x| match x.try_read() {
Ok(peer) => peer.info.direction == Direction::Outbound,
Err(_) => false,
})
.cloned()
.collect::<Vec<_>>();
res
}
/// Get a peer we're connected to by address.
pub fn get_connected_peer(&self, addr: &SocketAddr) -> Option<Arc<RwLock<Peer>>> {
self.peers.read().unwrap().get(addr).map(|p| p.clone())
@@ -298,6 +341,30 @@ impl Peers {
);
}
/// Broadcasts the provided stem transaction to our peer relay.
pub fn broadcast_stem_transaction(&self, tx: &core::Transaction) {
let dandelion_relay = self.get_dandelion_relay();
if dandelion_relay.is_empty() {
debug!(LOGGER, "No dandelion relay updating");
self.update_dandelion_relay();
}
// If still empty broadcast then broadcast transaction normally
if dandelion_relay.is_empty() {
self.broadcast_transaction(tx);
}
for relay in dandelion_relay.values() {
let relay = relay.read().unwrap();
if relay.is_connected() {
if let Err(e) = relay.send_stem_transaction(tx) {
debug!(
LOGGER,
"Error sending stem transaction to peer relay: {:?}", e
);
}
}
}
}
/// Broadcasts the provided transaction to PEER_PREFERRED_COUNT of our peers.
/// We may be connected to PEER_MAX_COUNT peers so we only
/// want to broadcast to a random subset of peers.
@@ -438,8 +505,8 @@ impl ChainAdapter for Peers {
fn total_height(&self) -> u64 {
self.adapter.total_height()
}
fn transaction_received(&self, tx: core::Transaction) {
self.adapter.transaction_received(tx)
fn transaction_received(&self, tx: core::Transaction, stem: bool) {
self.adapter.transaction_received(tx, stem)
}
fn block_received(&self, b: core::Block, peer_addr: SocketAddr) -> bool {
let hash = b.hash();
+7 -1
View File
@@ -63,7 +63,13 @@ impl MessageHandler for Protocol {
Type::Transaction => {
let tx: core::Transaction = msg.body()?;
adapter.transaction_received(tx);
adapter.transaction_received(tx, false);
Ok(None)
}
Type::StemTransaction => {
let tx: core::Transaction = msg.body()?;
adapter.transaction_received(tx, true);
Ok(None)
}
+1 -1
View File
@@ -205,7 +205,7 @@ impl ChainAdapter for DummyAdapter {
fn total_height(&self) -> u64 {
0
}
fn transaction_received(&self, _: core::Transaction) {}
fn transaction_received(&self, _: core::Transaction, stem: bool) {}
fn compact_block_received(&self, _cb: core::CompactBlock, _addr: SocketAddr) -> bool {
true
}
+1 -1
View File
@@ -167,7 +167,7 @@ pub trait ChainAdapter: Sync + Send {
fn total_height(&self) -> u64;
/// A valid transaction has been received from one of our peers
fn transaction_received(&self, tx: core::Transaction);
fn transaction_received(&self, tx: core::Transaction, stem: bool);
/// A block has been received from one of our peers. Returns true if the
/// block could be handled properly and is not deemed defective by the