2021-03-26 09:05:58 +01:00
|
|
|
use anyhow::{Context, Result};
|
2021-10-01 19:07:36 +02:00
|
|
|
use crossbeam_channel::{select, unbounded, Sender};
|
2021-03-26 09:05:58 +01:00
|
|
|
use rayon::prelude::*;
|
|
|
|
|
|
|
|
use std::{
|
|
|
|
collections::hash_map::HashMap,
|
|
|
|
io::{BufRead, BufReader, Write},
|
|
|
|
net::{Shutdown, TcpListener, TcpStream},
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
config::Config,
|
|
|
|
electrum::{Client, Rpc},
|
2021-10-05 20:39:52 +02:00
|
|
|
signals::ExitError,
|
2021-08-27 19:40:54 +02:00
|
|
|
thread::spawn,
|
2021-10-05 20:39:52 +02:00
|
|
|
tracker::Tracker,
|
2021-03-26 09:05:58 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Peer {
|
2021-05-25 17:09:32 +02:00
|
|
|
id: usize,
|
2021-03-26 09:05:58 +01:00
|
|
|
client: Client,
|
|
|
|
stream: TcpStream,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Peer {
|
2021-05-25 17:09:32 +02:00
|
|
|
fn new(id: usize, stream: TcpStream) -> Self {
|
2021-05-26 21:00:10 +02:00
|
|
|
let client = Client::default();
|
|
|
|
Self { id, client, stream }
|
2021-03-26 09:05:58 +01:00
|
|
|
}
|
|
|
|
|
2021-06-20 20:57:01 +02:00
|
|
|
fn send(&mut self, values: Vec<String>) -> Result<()> {
|
|
|
|
for mut value in values {
|
|
|
|
debug!("{}: send {}", self.id, value);
|
|
|
|
value += "\n";
|
|
|
|
self.stream
|
|
|
|
.write_all(value.as_bytes())
|
|
|
|
.with_context(|| format!("failed to send response: {:?}", value))?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-05-26 21:00:10 +02:00
|
|
|
fn disconnect(self) {
|
2021-10-01 09:17:06 +02:00
|
|
|
if let Err(e) = self.stream.shutdown(Shutdown::Both) {
|
|
|
|
warn!("{}: failed to shutdown TCP connection {}", self.id, e)
|
|
|
|
}
|
2021-05-25 17:09:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-05 20:39:52 +02:00
|
|
|
pub fn run() -> Result<()> {
|
|
|
|
let result = serve();
|
|
|
|
if let Err(e) = &result {
|
|
|
|
for cause in e.chain() {
|
|
|
|
if cause.downcast_ref::<ExitError>().is_some() {
|
|
|
|
info!("electrs stopped: {:?}", e);
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result.context("electrs failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serve() -> Result<()> {
|
|
|
|
let config = Config::from_args();
|
|
|
|
let tracker = Tracker::new(&config)?;
|
|
|
|
let mut rpc = Rpc::new(&config, tracker)?;
|
|
|
|
if config.sync_once {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
2021-03-26 09:05:58 +01:00
|
|
|
let (server_tx, server_rx) = unbounded();
|
2021-10-09 12:49:56 +02:00
|
|
|
if !config.disable_electrum_rpc {
|
|
|
|
let listener = TcpListener::bind(config.electrum_rpc_addr)?;
|
|
|
|
info!("serving Electrum RPC on {}", listener.local_addr()?);
|
|
|
|
spawn("accept_loop", || accept_loop(listener, server_tx)); // detach accepting thread
|
|
|
|
};
|
2021-03-26 09:05:58 +01:00
|
|
|
|
2021-10-09 12:49:56 +02:00
|
|
|
let new_block_rx = rpc.new_block_notification();
|
2021-03-26 09:05:58 +01:00
|
|
|
let mut peers = HashMap::<usize, Peer>::new();
|
|
|
|
loop {
|
|
|
|
select! {
|
2021-10-03 20:15:22 +02:00
|
|
|
recv(rpc.signal().receiver()) -> result => {
|
2021-09-21 11:47:42 +02:00
|
|
|
result.context("signal channel disconnected")?;
|
2021-10-05 20:39:52 +02:00
|
|
|
rpc.signal().exit_flag().poll().context("RPC server interrupted")?;
|
2021-03-26 09:05:58 +01:00
|
|
|
},
|
2021-10-01 19:07:36 +02:00
|
|
|
recv(new_block_rx) -> result => match result {
|
2021-03-26 09:05:58 +01:00
|
|
|
Ok(_) => (), // sync and update
|
|
|
|
Err(_) => break, // daemon is shutting down
|
|
|
|
},
|
|
|
|
recv(server_rx) -> event => {
|
|
|
|
let event = event.context("server disconnected")?;
|
2021-10-03 15:13:40 +02:00
|
|
|
handle_event(&rpc, &mut peers, event);
|
2021-03-26 09:05:58 +01:00
|
|
|
},
|
2021-10-01 19:07:36 +02:00
|
|
|
default(config.wait_duration) => (), // sync and update
|
2021-03-26 09:05:58 +01:00
|
|
|
};
|
2021-10-03 15:13:40 +02:00
|
|
|
if !server_rx.is_empty() {
|
|
|
|
continue; // continue RPC processing (if not done)
|
|
|
|
}
|
2021-03-26 09:05:58 +01:00
|
|
|
rpc.sync().context("rpc sync failed")?;
|
2021-05-26 21:00:10 +02:00
|
|
|
peers = notify_peers(&rpc, peers); // peers are disconnected on error.
|
2021-03-26 09:05:58 +01:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-05-25 17:09:32 +02:00
|
|
|
fn notify_peers(rpc: &Rpc, peers: HashMap<usize, Peer>) -> HashMap<usize, Peer> {
|
|
|
|
peers
|
|
|
|
.into_par_iter()
|
2021-07-20 17:56:43 +02:00
|
|
|
.filter_map(|(_, mut peer)| match notify_peer(rpc, &mut peer) {
|
2021-05-26 21:00:10 +02:00
|
|
|
Ok(()) => Some((peer.id, peer)),
|
2021-05-25 17:09:32 +02:00
|
|
|
Err(e) => {
|
2021-05-26 21:00:10 +02:00
|
|
|
error!("failed to notify peer {}: {}", peer.id, e);
|
|
|
|
peer.disconnect();
|
2021-05-25 17:09:32 +02:00
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2021-05-26 21:00:10 +02:00
|
|
|
fn notify_peer(rpc: &Rpc, peer: &mut Peer) -> Result<()> {
|
2021-05-25 17:09:32 +02:00
|
|
|
let notifications = rpc
|
|
|
|
.update_client(&mut peer.client)
|
|
|
|
.context("failed to generate notifications")?;
|
2021-06-20 20:57:01 +02:00
|
|
|
peer.send(notifications)
|
|
|
|
.context("failed to send notifications")
|
2021-05-25 17:09:32 +02:00
|
|
|
}
|
|
|
|
|
2021-03-26 09:05:58 +01:00
|
|
|
struct Event {
|
|
|
|
peer_id: usize,
|
|
|
|
msg: Message,
|
|
|
|
}
|
|
|
|
|
|
|
|
enum Message {
|
|
|
|
New(TcpStream),
|
|
|
|
Request(String),
|
|
|
|
Done,
|
|
|
|
}
|
|
|
|
|
2021-05-25 17:09:32 +02:00
|
|
|
fn handle_event(rpc: &Rpc, peers: &mut HashMap<usize, Peer>, event: Event) {
|
2021-05-26 21:00:10 +02:00
|
|
|
let Event { msg, peer_id } = event;
|
|
|
|
match msg {
|
2021-03-26 09:05:58 +01:00
|
|
|
Message::New(stream) => {
|
2021-05-26 21:00:10 +02:00
|
|
|
debug!("{}: connected", peer_id);
|
|
|
|
peers.insert(peer_id, Peer::new(peer_id, stream));
|
2021-03-26 09:05:58 +01:00
|
|
|
}
|
|
|
|
Message::Request(line) => {
|
2021-05-26 21:00:10 +02:00
|
|
|
let result = match peers.get_mut(&peer_id) {
|
2021-06-20 20:57:01 +02:00
|
|
|
Some(peer) => handle_request(rpc, peer, &line),
|
2021-05-26 21:00:10 +02:00
|
|
|
None => return, // unknown peer
|
2021-03-26 09:05:58 +01:00
|
|
|
};
|
|
|
|
if let Err(e) = result {
|
2021-05-26 21:00:10 +02:00
|
|
|
error!("{}: disconnecting due to {}", peer_id, e);
|
|
|
|
peers.remove(&peer_id).unwrap().disconnect();
|
2021-03-26 09:05:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Message::Done => {
|
2021-05-26 21:00:10 +02:00
|
|
|
// already disconnected, just remove from peers' map
|
|
|
|
peers.remove(&peer_id);
|
2021-03-26 09:05:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-20 20:57:01 +02:00
|
|
|
fn handle_request(rpc: &Rpc, peer: &mut Peer, line: &str) -> Result<()> {
|
2021-07-20 17:56:43 +02:00
|
|
|
let response = rpc.handle_request(&mut peer.client, line);
|
2021-06-20 20:57:01 +02:00
|
|
|
peer.send(vec![response])
|
2021-03-26 09:05:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn accept_loop(listener: TcpListener, server_tx: Sender<Event>) -> Result<()> {
|
|
|
|
for (peer_id, conn) in listener.incoming().enumerate() {
|
|
|
|
let stream = conn.context("failed to accept")?;
|
|
|
|
let tx = server_tx.clone();
|
|
|
|
spawn("recv_loop", move || {
|
|
|
|
let result = recv_loop(peer_id, &stream, tx);
|
2021-10-01 09:17:06 +02:00
|
|
|
if let Err(e) = stream.shutdown(Shutdown::Read) {
|
|
|
|
warn!("{}: failed to shutdown TCP receiving {}", peer_id, e)
|
|
|
|
}
|
2021-03-26 09:05:58 +01:00
|
|
|
result
|
|
|
|
});
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn recv_loop(peer_id: usize, stream: &TcpStream, server_tx: Sender<Event>) -> Result<()> {
|
2021-05-26 21:00:10 +02:00
|
|
|
let msg = Message::New(stream.try_clone()?);
|
|
|
|
server_tx.send(Event { peer_id, msg })?;
|
|
|
|
|
|
|
|
for line in BufReader::new(stream).lines() {
|
2021-03-26 09:05:58 +01:00
|
|
|
let line = line.with_context(|| format!("{}: recv failed", peer_id))?;
|
|
|
|
debug!("{}: recv {}", peer_id, line);
|
|
|
|
let msg = Message::Request(line);
|
|
|
|
server_tx.send(Event { peer_id, msg })?;
|
|
|
|
}
|
2021-05-26 21:00:10 +02:00
|
|
|
|
|
|
|
debug!("{}: disconnected", peer_id);
|
|
|
|
let msg = Message::Done;
|
|
|
|
server_tx.send(Event { peer_id, msg })?;
|
2021-03-26 09:05:58 +01:00
|
|
|
Ok(())
|
|
|
|
}
|