Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,3 +14,4 @@ script:
- cargo build --verbose
- cargo test --verbose
- if [ "$(rustup show | grep default | grep 1.29.2)" != "" ]; then cd fuzz && cargo test --verbose && ./travis-fuzz.sh; fi
- if [ "$(rustup show | grep default | grep stable)" != "" ]; then cd net-tokio && cargo build --verbose; fi
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
[package]
name = "lightning"
version = "0.0.8"
version = "0.0.9"
authors = ["Matt Corallo"]
license = "Apache-2.0"
repository = "https://github.com/rust-bitcoin/rust-lightning/"
Expand All@@ -22,13 +22,13 @@ max_level_info = []
max_level_debug = []

[dependencies]
bitcoin = "0.17"
bitcoin = "0.18"
bitcoin_hashes = "0.3"
rand = "0.4"
secp256k1 = "0.12"

[dev-dependencies.bitcoin]
version = "0.17"
version = "0.18"
features = ["bitcoinconsensus"]

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion fuzz/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ libfuzzer_fuzz = ["libfuzzer-sys"]
[dependencies]
afl = { version = "0.4", optional = true }
lightning = { path = "..", features = ["fuzztarget"] }
bitcoin = { version = "0.17", features = ["fuzztarget"] }
bitcoin = { version = "0.18", features = ["fuzztarget"] }
bitcoin_hashes = { version = "0.3", features=["fuzztarget"] }
hex = "0.3"
honggfuzz = { version = "0.5", optional = true }
Expand Down
19 changes: 19 additions & 0 deletions net-tokio/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
[package]
name = "lightning-net-tokio"
version = "0.0.1"
authors = ["Matt Corallo"]
license = "Apache-2.0"
description = """
Implementation of the rust-lightning network stack using Tokio.
For Rust-Lightning clients which wish to make direct connections to Lightning P2P nodes, this is a simple alternative to implementing the nerequired network stack, especially for those already using Tokio.
"""

[dependencies]
bitcoin = "0.18"
bitcoin_hashes = "0.3"
lightning = { version = "0.0.9", path = "../" }
secp256k1 = "0.12"
tokio-codec = "0.1"
futures = "0.1"
tokio = "0.1"
bytes = "0.4"
270 changes: 270 additions & 0 deletions net-tokio/src/lib.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
extern crate bytes;
extern crate tokio;
extern crate tokio_codec;
extern crate futures;
extern crate lightning;
extern crate secp256k1;

use bytes::BufMut;

use futures::future;
use futures::future::Future;
use futures::{AsyncSink, Stream, Sink};
use futures::sync::mpsc;

use secp256k1::key::PublicKey;

use tokio::timer::Delay;
use tokio::net::TcpStream;

use lightning::ln::peer_handler;
use lightning::ln::peer_handler::SocketDescriptor as LnSocketTrait;

use std::mem;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
use std::vec::Vec;
use std::hash::Hash;

static ID_COUNTER: AtomicU64 = AtomicU64::new(0);

/// A connection to a remote peer. Can be constructed either as a remote connection using
/// Connection::setup_outbound o
pub struct Connection {
writer: Option<mpsc::Sender<bytes::Bytes>>,
event_notify: mpsc::Sender<()>,
pending_read: Vec<u8>,
read_blocker: Option<futures::sync::oneshot::Sender<Result<(), ()>>>,
read_paused: bool,
need_disconnect: bool,
id: u64,
}
impl Connection {
fn schedule_read(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, us: Arc<Mutex<Self>>, reader: futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>) {
let us_ref = us.clone();
let us_close_ref = us.clone();
let peer_manager_ref = peer_manager.clone();
tokio::spawn(reader.for_each(move |b| {
let pending_read = b.to_vec();
{
let mut lock = us_ref.lock().unwrap();
assert!(lock.pending_read.is_empty());
if lock.read_paused {
lock.pending_read = pending_read;
let (sender, blocker) = futures::sync::oneshot::channel();
lock.read_blocker = Some(sender);
return future::Either::A(blocker.then(|_| { Ok(()) }));
}
}
//TODO: There's a race where we don't meet the requirements of disconnect_socket if its
//called right here, after we release the us_ref lock in the scope above, but before we
//call read_event!
match peer_manager.read_event(&mut SocketDescriptor::new(us_ref.clone(), peer_manager.clone()), pending_read) {
Ok(pause_read) => {
if pause_read {
let mut lock = us_ref.lock().unwrap();
lock.read_paused = true;
}
},
Err(e) => {
us_ref.lock().unwrap().need_disconnect = false;
return future::Either::B(future::result(Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e))));
}
}

if let Err(e) = us_ref.lock().unwrap().event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}

future::Either::B(future::result(Ok(())))
}).then(move |_| {
if us_close_ref.lock().unwrap().need_disconnect {
peer_manager_ref.disconnect_event(&SocketDescriptor::new(us_close_ref, peer_manager_ref.clone()));
println!("Peer disconnected!");
} else {
println!("We disconnected peer!");
}
Ok(())
}));
}

fn new(event_notify: mpsc::Sender<()>, stream: TcpStream) -> (futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>, Arc<Mutex<Self>>) {
let (writer, reader) = tokio_codec::Framed::new(stream, tokio_codec::BytesCodec::new()).split();
let (send_sink, send_stream) = mpsc::channel(3);
tokio::spawn(writer.send_all(send_stream.map_err(|_| -> std::io::Error {
unreachable!();
})).then(|_| {
future::result(Ok(()))
}));
let us = Arc::new(Mutex::new(Self { writer: Some(send_sink), event_notify, pending_read: Vec::new(), read_blocker: None, read_paused: false, need_disconnect: true, id: ID_COUNTER.fetch_add(1, Ordering::AcqRel) }));

(reader, us)
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// accepting an incoming connection (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_inbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(_) = peer_manager.new_inbound_connection(SocketDescriptor::new(us.clone(), peer_manager.clone())) {
Self::schedule_read(peer_manager, us, reader);
}
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// making an outbound connection which is expected to be accepted by a peer with the given
/// public key (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(initial_send) = peer_manager.new_outbound_connection(their_node_id, SocketDescriptor::new(us.clone(), peer_manager.clone())) {
if SocketDescriptor::new(us.clone(), peer_manager.clone()).send_data(&initial_send, 0, true) == initial_send.len() {
Self::schedule_read(peer_manager, us, reader);
} else {
println!("Failed to write first full message to socket!");
}
}
}

/// Process incoming messages and feed outgoing messages on a new connection made to the given
/// socket address which is expected to be accepted by a peer with the given public key (by
/// scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn connect_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, addr: SocketAddr) {
let connect_timeout = Delay::new(Instant::now() + Duration::from_secs(10)).then(|_| {
future::err(std::io::Error::new(std::io::ErrorKind::TimedOut, "timeout reached"))
});
tokio::spawn(TcpStream::connect(&addr).select(connect_timeout)
.and_then(move |stream| {
Connection::setup_outbound(peer_manager, event_notify, their_node_id, stream.0);
future::ok(())
}).or_else(|_| {
//TODO: return errors somehow
future::ok(())
}));
}
}

#[derive(Clone)]
pub struct SocketDescriptor {
conn: Arc<Mutex<Connection>>,
id: u64,
peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>,
}
impl SocketDescriptor {
fn new(conn: Arc<Mutex<Connection>>, peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>) -> Self {
let id = conn.lock().unwrap().id;
Self { conn, id, peer_manager }
}
}
impl peer_handler::SocketDescriptor for SocketDescriptor {
fn send_data(&mut self, data: &Vec<u8>, write_offset: usize, resume_read: bool) -> usize {
macro_rules! schedule_read {
($us_ref: expr) => {
tokio::spawn(future::lazy(move || -> Result<(), ()> {
let mut read_data = Vec::new();
{
let mut us = $us_ref.conn.lock().unwrap();
mem::swap(&mut read_data, &mut us.pending_read);
}
if !read_data.is_empty() {
let mut us_clone = $us_ref.clone();
match $us_ref.peer_manager.read_event(&mut us_clone, read_data) {
Ok(pause_read) => {
if pause_read { return Ok(()); }
},
Err(_) => {
//TODO: Not actually sure how to do this
return Ok(());
}
}
}
let mut us = $us_ref.conn.lock().unwrap();
if let Some(sender) = us.read_blocker.take() {
sender.send(Ok(())).unwrap();
}
us.read_paused = false;
if let Err(e) = us.event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}
Ok(())
}));
}
}

let mut us = self.conn.lock().unwrap();
if resume_read {
let us_ref = self.clone();
schedule_read!(us_ref);
}
if data.len() == write_offset { return 0; }
if us.writer.is_none() {
us.read_paused = true;
return 0;
}

let mut bytes = bytes::BytesMut::with_capacity(data.len() - write_offset);
bytes.put(&data[write_offset..]);
let write_res = us.writer.as_mut().unwrap().start_send(bytes.freeze());
match write_res {
Ok(res) => {
match res {
AsyncSink::Ready => {
data.len() - write_offset
},
AsyncSink::NotReady(_) => {
us.read_paused = true;
let us_ref = self.clone();
tokio::spawn(us.writer.take().unwrap().flush().then(move |writer_res| -> Result<(), ()> {
if let Ok(writer) = writer_res {
{
let mut us = us_ref.conn.lock().unwrap();
us.writer = Some(writer);
}
schedule_read!(us_ref);
} // we'll fire the disconnect event on the socket reader end
Ok(())
}));
0
}
}
},
Err(_) => {
// We'll fire the disconnected event on the socket reader end
0
},
}
}

fn disconnect_socket(&mut self) {
let mut us = self.conn.lock().unwrap();
us.need_disconnect = true;
us.read_paused = true;
}
}
impl Eq for SocketDescriptor {}
impl PartialEq for SocketDescriptor {
fn eq(&self, o: &Self) -> bool {
self.id == o.id
}
}
impl Hash for SocketDescriptor {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,3 +14,4 @@ script:
- cargo build --verbose
- cargo test --verbose
- if [ "$(rustup show | grep default | grep 1.29.2)" != "" ]; then cd fuzz && cargo test --verbose && ./travis-fuzz.sh; fi
- if [ "$(rustup show | grep default | grep stable)" != "" ]; then cd net-tokio && cargo build --verbose; fi
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
[package]
name = "lightning"
version = "0.0.8"
version = "0.0.9"
authors = ["Matt Corallo"]
license = "Apache-2.0"
repository = "https://github.com/rust-bitcoin/rust-lightning/"
Expand All@@ -22,13 +22,13 @@ max_level_info = []
max_level_debug = []

[dependencies]
bitcoin = "0.17"
bitcoin = "0.18"
bitcoin_hashes = "0.3"
rand = "0.4"
secp256k1 = "0.12"

[dev-dependencies.bitcoin]
version = "0.17"
version = "0.18"
features = ["bitcoinconsensus"]

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion fuzz/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ libfuzzer_fuzz = ["libfuzzer-sys"]
[dependencies]
afl = { version = "0.4", optional = true }
lightning = { path = "..", features = ["fuzztarget"] }
bitcoin = { version = "0.17", features = ["fuzztarget"] }
bitcoin = { version = "0.18", features = ["fuzztarget"] }
bitcoin_hashes = { version = "0.3", features=["fuzztarget"] }
hex = "0.3"
honggfuzz = { version = "0.5", optional = true }
Expand Down
19 changes: 19 additions & 0 deletions net-tokio/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
[package]
name = "lightning-net-tokio"
version = "0.0.1"
authors = ["Matt Corallo"]
license = "Apache-2.0"
description = """
Implementation of the rust-lightning network stack using Tokio.
For Rust-Lightning clients which wish to make direct connections to Lightning P2P nodes, this is a simple alternative to implementing the nerequired network stack, especially for those already using Tokio.
"""

[dependencies]
bitcoin = "0.18"
bitcoin_hashes = "0.3"
lightning = { version = "0.0.9", path = "../" }
secp256k1 = "0.12"
tokio-codec = "0.1"
futures = "0.1"
tokio = "0.1"
bytes = "0.4"
270 changes: 270 additions & 0 deletions net-tokio/src/lib.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
extern crate bytes;
extern crate tokio;
extern crate tokio_codec;
extern crate futures;
extern crate lightning;
extern crate secp256k1;

use bytes::BufMut;

use futures::future;
use futures::future::Future;
use futures::{AsyncSink, Stream, Sink};
use futures::sync::mpsc;

use secp256k1::key::PublicKey;

use tokio::timer::Delay;
use tokio::net::TcpStream;

use lightning::ln::peer_handler;
use lightning::ln::peer_handler::SocketDescriptor as LnSocketTrait;

use std::mem;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
use std::vec::Vec;
use std::hash::Hash;

static ID_COUNTER: AtomicU64 = AtomicU64::new(0);

/// A connection to a remote peer. Can be constructed either as a remote connection using
/// Connection::setup_outbound o
pub struct Connection {
writer: Option<mpsc::Sender<bytes::Bytes>>,
event_notify: mpsc::Sender<()>,
pending_read: Vec<u8>,
read_blocker: Option<futures::sync::oneshot::Sender<Result<(), ()>>>,
read_paused: bool,
need_disconnect: bool,
id: u64,
}
impl Connection {
fn schedule_read(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, us: Arc<Mutex<Self>>, reader: futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>) {
let us_ref = us.clone();
let us_close_ref = us.clone();
let peer_manager_ref = peer_manager.clone();
tokio::spawn(reader.for_each(move |b| {
let pending_read = b.to_vec();
{
let mut lock = us_ref.lock().unwrap();
assert!(lock.pending_read.is_empty());
if lock.read_paused {
lock.pending_read = pending_read;
let (sender, blocker) = futures::sync::oneshot::channel();
lock.read_blocker = Some(sender);
return future::Either::A(blocker.then(|_| { Ok(()) }));
}
}
//TODO: There's a race where we don't meet the requirements of disconnect_socket if its
//called right here, after we release the us_ref lock in the scope above, but before we
//call read_event!
match peer_manager.read_event(&mut SocketDescriptor::new(us_ref.clone(), peer_manager.clone()), pending_read) {
Ok(pause_read) => {
if pause_read {
let mut lock = us_ref.lock().unwrap();
lock.read_paused = true;
}
},
Err(e) => {
us_ref.lock().unwrap().need_disconnect = false;
return future::Either::B(future::result(Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e))));
}
}

if let Err(e) = us_ref.lock().unwrap().event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}

future::Either::B(future::result(Ok(())))
}).then(move |_| {
if us_close_ref.lock().unwrap().need_disconnect {
peer_manager_ref.disconnect_event(&SocketDescriptor::new(us_close_ref, peer_manager_ref.clone()));
println!("Peer disconnected!");
} else {
println!("We disconnected peer!");
}
Ok(())
}));
}

fn new(event_notify: mpsc::Sender<()>, stream: TcpStream) -> (futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>, Arc<Mutex<Self>>) {
let (writer, reader) = tokio_codec::Framed::new(stream, tokio_codec::BytesCodec::new()).split();
let (send_sink, send_stream) = mpsc::channel(3);
tokio::spawn(writer.send_all(send_stream.map_err(|_| -> std::io::Error {
unreachable!();
})).then(|_| {
future::result(Ok(()))
}));
let us = Arc::new(Mutex::new(Self { writer: Some(send_sink), event_notify, pending_read: Vec::new(), read_blocker: None, read_paused: false, need_disconnect: true, id: ID_COUNTER.fetch_add(1, Ordering::AcqRel) }));

(reader, us)
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// accepting an incoming connection (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_inbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(_) = peer_manager.new_inbound_connection(SocketDescriptor::new(us.clone(), peer_manager.clone())) {
Self::schedule_read(peer_manager, us, reader);
}
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// making an outbound connection which is expected to be accepted by a peer with the given
/// public key (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(initial_send) = peer_manager.new_outbound_connection(their_node_id, SocketDescriptor::new(us.clone(), peer_manager.clone())) {
if SocketDescriptor::new(us.clone(), peer_manager.clone()).send_data(&initial_send, 0, true) == initial_send.len() {
Self::schedule_read(peer_manager, us, reader);
} else {
println!("Failed to write first full message to socket!");
}
}
}

/// Process incoming messages and feed outgoing messages on a new connection made to the given
/// socket address which is expected to be accepted by a peer with the given public key (by
/// scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn connect_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, addr: SocketAddr) {
let connect_timeout = Delay::new(Instant::now() + Duration::from_secs(10)).then(|_| {
future::err(std::io::Error::new(std::io::ErrorKind::TimedOut, "timeout reached"))
});
tokio::spawn(TcpStream::connect(&addr).select(connect_timeout)
.and_then(move |stream| {
Connection::setup_outbound(peer_manager, event_notify, their_node_id, stream.0);
future::ok(())
}).or_else(|_| {
//TODO: return errors somehow
future::ok(())
}));
}
}

#[derive(Clone)]
pub struct SocketDescriptor {
conn: Arc<Mutex<Connection>>,
id: u64,
peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>,
}
impl SocketDescriptor {
fn new(conn: Arc<Mutex<Connection>>, peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>) -> Self {
let id = conn.lock().unwrap().id;
Self { conn, id, peer_manager }
}
}
impl peer_handler::SocketDescriptor for SocketDescriptor {
fn send_data(&mut self, data: &Vec<u8>, write_offset: usize, resume_read: bool) -> usize {
macro_rules! schedule_read {
($us_ref: expr) => {
tokio::spawn(future::lazy(move || -> Result<(), ()> {
let mut read_data = Vec::new();
{
let mut us = $us_ref.conn.lock().unwrap();
mem::swap(&mut read_data, &mut us.pending_read);
}
if !read_data.is_empty() {
let mut us_clone = $us_ref.clone();
match $us_ref.peer_manager.read_event(&mut us_clone, read_data) {
Ok(pause_read) => {
if pause_read { return Ok(()); }
},
Err(_) => {
//TODO: Not actually sure how to do this
return Ok(());
}
}
}
let mut us = $us_ref.conn.lock().unwrap();
if let Some(sender) = us.read_blocker.take() {
sender.send(Ok(())).unwrap();
}
us.read_paused = false;
if let Err(e) = us.event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}
Ok(())
}));
}
}

let mut us = self.conn.lock().unwrap();
if resume_read {
let us_ref = self.clone();
schedule_read!(us_ref);
}
if data.len() == write_offset { return 0; }
if us.writer.is_none() {
us.read_paused = true;
return 0;
}

let mut bytes = bytes::BytesMut::with_capacity(data.len() - write_offset);
bytes.put(&data[write_offset..]);
let write_res = us.writer.as_mut().unwrap().start_send(bytes.freeze());
match write_res {
Ok(res) => {
match res {
AsyncSink::Ready => {
data.len() - write_offset
},
AsyncSink::NotReady(_) => {
us.read_paused = true;
let us_ref = self.clone();
tokio::spawn(us.writer.take().unwrap().flush().then(move |writer_res| -> Result<(), ()> {
if let Ok(writer) = writer_res {
{
let mut us = us_ref.conn.lock().unwrap();
us.writer = Some(writer);
}
schedule_read!(us_ref);
} // we'll fire the disconnect event on the socket reader end
Ok(())
}));
0
}
}
},
Err(_) => {
// We'll fire the disconnected event on the socket reader end
0
},
}
}

fn disconnect_socket(&mut self) {
let mut us = self.conn.lock().unwrap();
us.need_disconnect = true;
us.read_paused = true;
}
}
impl Eq for SocketDescriptor {}
impl PartialEq for SocketDescriptor {
fn eq(&self, o: &Self) -> bool {
self.id == o.id
}
}
impl Hash for SocketDescriptor {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,3 +14,4 @@ script:
- cargo build --verbose
- cargo test --verbose
- if [ "$(rustup show | grep default | grep 1.29.2)" != "" ]; then cd fuzz && cargo test --verbose && ./travis-fuzz.sh; fi
- if [ "$(rustup show | grep default | grep stable)" != "" ]; then cd net-tokio && cargo build --verbose; fi
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
[package]
name = "lightning"
version = "0.0.8"
version = "0.0.9"
authors = ["Matt Corallo"]
license = "Apache-2.0"
repository = "https://github.com/rust-bitcoin/rust-lightning/"
Expand All@@ -22,13 +22,13 @@ max_level_info = []
max_level_debug = []

[dependencies]
bitcoin = "0.17"
bitcoin = "0.18"
bitcoin_hashes = "0.3"
rand = "0.4"
secp256k1 = "0.12"

[dev-dependencies.bitcoin]
version = "0.17"
version = "0.18"
features = ["bitcoinconsensus"]

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion fuzz/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ libfuzzer_fuzz = ["libfuzzer-sys"]
[dependencies]
afl = { version = "0.4", optional = true }
lightning = { path = "..", features = ["fuzztarget"] }
bitcoin = { version = "0.17", features = ["fuzztarget"] }
bitcoin = { version = "0.18", features = ["fuzztarget"] }
bitcoin_hashes = { version = "0.3", features=["fuzztarget"] }
hex = "0.3"
honggfuzz = { version = "0.5", optional = true }
Expand Down
19 changes: 19 additions & 0 deletions net-tokio/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
[package]
name = "lightning-net-tokio"
version = "0.0.1"
authors = ["Matt Corallo"]
license = "Apache-2.0"
description = """
Implementation of the rust-lightning network stack using Tokio.
For Rust-Lightning clients which wish to make direct connections to Lightning P2P nodes, this is a simple alternative to implementing the nerequired network stack, especially for those already using Tokio.
"""

[dependencies]
bitcoin = "0.18"
bitcoin_hashes = "0.3"
lightning = { version = "0.0.9", path = "../" }
secp256k1 = "0.12"
tokio-codec = "0.1"
futures = "0.1"
tokio = "0.1"
bytes = "0.4"
270 changes: 270 additions & 0 deletions net-tokio/src/lib.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
extern crate bytes;
extern crate tokio;
extern crate tokio_codec;
extern crate futures;
extern crate lightning;
extern crate secp256k1;

use bytes::BufMut;

use futures::future;
use futures::future::Future;
use futures::{AsyncSink, Stream, Sink};
use futures::sync::mpsc;

use secp256k1::key::PublicKey;

use tokio::timer::Delay;
use tokio::net::TcpStream;

use lightning::ln::peer_handler;
use lightning::ln::peer_handler::SocketDescriptor as LnSocketTrait;

use std::mem;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
use std::vec::Vec;
use std::hash::Hash;

static ID_COUNTER: AtomicU64 = AtomicU64::new(0);

/// A connection to a remote peer. Can be constructed either as a remote connection using
/// Connection::setup_outbound o
pub struct Connection {
writer: Option<mpsc::Sender<bytes::Bytes>>,
event_notify: mpsc::Sender<()>,
pending_read: Vec<u8>,
read_blocker: Option<futures::sync::oneshot::Sender<Result<(), ()>>>,
read_paused: bool,
need_disconnect: bool,
id: u64,
}
impl Connection {
fn schedule_read(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, us: Arc<Mutex<Self>>, reader: futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>) {
let us_ref = us.clone();
let us_close_ref = us.clone();
let peer_manager_ref = peer_manager.clone();
tokio::spawn(reader.for_each(move |b| {
let pending_read = b.to_vec();
{
let mut lock = us_ref.lock().unwrap();
assert!(lock.pending_read.is_empty());
if lock.read_paused {
lock.pending_read = pending_read;
let (sender, blocker) = futures::sync::oneshot::channel();
lock.read_blocker = Some(sender);
return future::Either::A(blocker.then(|_| { Ok(()) }));
}
}
//TODO: There's a race where we don't meet the requirements of disconnect_socket if its
//called right here, after we release the us_ref lock in the scope above, but before we
//call read_event!
match peer_manager.read_event(&mut SocketDescriptor::new(us_ref.clone(), peer_manager.clone()), pending_read) {
Ok(pause_read) => {
if pause_read {
let mut lock = us_ref.lock().unwrap();
lock.read_paused = true;
}
},
Err(e) => {
us_ref.lock().unwrap().need_disconnect = false;
return future::Either::B(future::result(Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e))));
}
}

if let Err(e) = us_ref.lock().unwrap().event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}

future::Either::B(future::result(Ok(())))
}).then(move |_| {
if us_close_ref.lock().unwrap().need_disconnect {
peer_manager_ref.disconnect_event(&SocketDescriptor::new(us_close_ref, peer_manager_ref.clone()));
println!("Peer disconnected!");
} else {
println!("We disconnected peer!");
}
Ok(())
}));
}

fn new(event_notify: mpsc::Sender<()>, stream: TcpStream) -> (futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>, Arc<Mutex<Self>>) {
let (writer, reader) = tokio_codec::Framed::new(stream, tokio_codec::BytesCodec::new()).split();
let (send_sink, send_stream) = mpsc::channel(3);
tokio::spawn(writer.send_all(send_stream.map_err(|_| -> std::io::Error {
unreachable!();
})).then(|_| {
future::result(Ok(()))
}));
let us = Arc::new(Mutex::new(Self { writer: Some(send_sink), event_notify, pending_read: Vec::new(), read_blocker: None, read_paused: false, need_disconnect: true, id: ID_COUNTER.fetch_add(1, Ordering::AcqRel) }));

(reader, us)
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// accepting an incoming connection (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_inbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(_) = peer_manager.new_inbound_connection(SocketDescriptor::new(us.clone(), peer_manager.clone())) {
Self::schedule_read(peer_manager, us, reader);
}
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// making an outbound connection which is expected to be accepted by a peer with the given
/// public key (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(initial_send) = peer_manager.new_outbound_connection(their_node_id, SocketDescriptor::new(us.clone(), peer_manager.clone())) {
if SocketDescriptor::new(us.clone(), peer_manager.clone()).send_data(&initial_send, 0, true) == initial_send.len() {
Self::schedule_read(peer_manager, us, reader);
} else {
println!("Failed to write first full message to socket!");
}
}
}

/// Process incoming messages and feed outgoing messages on a new connection made to the given
/// socket address which is expected to be accepted by a peer with the given public key (by
/// scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn connect_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, addr: SocketAddr) {
let connect_timeout = Delay::new(Instant::now() + Duration::from_secs(10)).then(|_| {
future::err(std::io::Error::new(std::io::ErrorKind::TimedOut, "timeout reached"))
});
tokio::spawn(TcpStream::connect(&addr).select(connect_timeout)
.and_then(move |stream| {
Connection::setup_outbound(peer_manager, event_notify, their_node_id, stream.0);
future::ok(())
}).or_else(|_| {
//TODO: return errors somehow
future::ok(())
}));
}
}

#[derive(Clone)]
pub struct SocketDescriptor {
conn: Arc<Mutex<Connection>>,
id: u64,
peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>,
}
impl SocketDescriptor {
fn new(conn: Arc<Mutex<Connection>>, peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>) -> Self {
let id = conn.lock().unwrap().id;
Self { conn, id, peer_manager }
}
}
impl peer_handler::SocketDescriptor for SocketDescriptor {
fn send_data(&mut self, data: &Vec<u8>, write_offset: usize, resume_read: bool) -> usize {
macro_rules! schedule_read {
($us_ref: expr) => {
tokio::spawn(future::lazy(move || -> Result<(), ()> {
let mut read_data = Vec::new();
{
let mut us = $us_ref.conn.lock().unwrap();
mem::swap(&mut read_data, &mut us.pending_read);
}
if !read_data.is_empty() {
let mut us_clone = $us_ref.clone();
match $us_ref.peer_manager.read_event(&mut us_clone, read_data) {
Ok(pause_read) => {
if pause_read { return Ok(()); }
},
Err(_) => {
//TODO: Not actually sure how to do this
return Ok(());
}
}
}
let mut us = $us_ref.conn.lock().unwrap();
if let Some(sender) = us.read_blocker.take() {
sender.send(Ok(())).unwrap();
}
us.read_paused = false;
if let Err(e) = us.event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}
Ok(())
}));
}
}

let mut us = self.conn.lock().unwrap();
if resume_read {
let us_ref = self.clone();
schedule_read!(us_ref);
}
if data.len() == write_offset { return 0; }
if us.writer.is_none() {
us.read_paused = true;
return 0;
}

let mut bytes = bytes::BytesMut::with_capacity(data.len() - write_offset);
bytes.put(&data[write_offset..]);
let write_res = us.writer.as_mut().unwrap().start_send(bytes.freeze());
match write_res {
Ok(res) => {
match res {
AsyncSink::Ready => {
data.len() - write_offset
},
AsyncSink::NotReady(_) => {
us.read_paused = true;
let us_ref = self.clone();
tokio::spawn(us.writer.take().unwrap().flush().then(move |writer_res| -> Result<(), ()> {
if let Ok(writer) = writer_res {
{
let mut us = us_ref.conn.lock().unwrap();
us.writer = Some(writer);
}
schedule_read!(us_ref);
} // we'll fire the disconnect event on the socket reader end
Ok(())
}));
0
}
}
},
Err(_) => {
// We'll fire the disconnected event on the socket reader end
0
},
}
}

fn disconnect_socket(&mut self) {
let mut us = self.conn.lock().unwrap();
us.need_disconnect = true;
us.read_paused = true;
}
}
impl Eq for SocketDescriptor {}
impl PartialEq for SocketDescriptor {
fn eq(&self, o: &Self) -> bool {
self.id == o.id
}
}
impl Hash for SocketDescriptor {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,3 +14,4 @@ script:
- cargo build --verbose
- cargo test --verbose
- if [ "$(rustup show | grep default | grep 1.29.2)" != "" ]; then cd fuzz && cargo test --verbose && ./travis-fuzz.sh; fi
- if [ "$(rustup show | grep default | grep stable)" != "" ]; then cd net-tokio && cargo build --verbose; fi
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
[package]
name = "lightning"
version = "0.0.8"
version = "0.0.9"
authors = ["Matt Corallo"]
license = "Apache-2.0"
repository = "https://github.com/rust-bitcoin/rust-lightning/"
Expand All@@ -22,13 +22,13 @@ max_level_info = []
max_level_debug = []

[dependencies]
bitcoin = "0.17"
bitcoin = "0.18"
bitcoin_hashes = "0.3"
rand = "0.4"
secp256k1 = "0.12"

[dev-dependencies.bitcoin]
version = "0.17"
version = "0.18"
features = ["bitcoinconsensus"]

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion fuzz/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ libfuzzer_fuzz = ["libfuzzer-sys"]
[dependencies]
afl = { version = "0.4", optional = true }
lightning = { path = "..", features = ["fuzztarget"] }
bitcoin = { version = "0.17", features = ["fuzztarget"] }
bitcoin = { version = "0.18", features = ["fuzztarget"] }
bitcoin_hashes = { version = "0.3", features=["fuzztarget"] }
hex = "0.3"
honggfuzz = { version = "0.5", optional = true }
Expand Down
19 changes: 19 additions & 0 deletions net-tokio/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
[package]
name = "lightning-net-tokio"
version = "0.0.1"
authors = ["Matt Corallo"]
license = "Apache-2.0"
description = """
Implementation of the rust-lightning network stack using Tokio.
For Rust-Lightning clients which wish to make direct connections to Lightning P2P nodes, this is a simple alternative to implementing the nerequired network stack, especially for those already using Tokio.
"""

[dependencies]
bitcoin = "0.18"
bitcoin_hashes = "0.3"
lightning = { version = "0.0.9", path = "../" }
secp256k1 = "0.12"
tokio-codec = "0.1"
futures = "0.1"
tokio = "0.1"
bytes = "0.4"
270 changes: 270 additions & 0 deletions net-tokio/src/lib.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
extern crate bytes;
extern crate tokio;
extern crate tokio_codec;
extern crate futures;
extern crate lightning;
extern crate secp256k1;

use bytes::BufMut;

use futures::future;
use futures::future::Future;
use futures::{AsyncSink, Stream, Sink};
use futures::sync::mpsc;

use secp256k1::key::PublicKey;

use tokio::timer::Delay;
use tokio::net::TcpStream;

use lightning::ln::peer_handler;
use lightning::ln::peer_handler::SocketDescriptor as LnSocketTrait;

use std::mem;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
use std::vec::Vec;
use std::hash::Hash;

static ID_COUNTER: AtomicU64 = AtomicU64::new(0);

/// A connection to a remote peer. Can be constructed either as a remote connection using
/// Connection::setup_outbound o
pub struct Connection {
writer: Option<mpsc::Sender<bytes::Bytes>>,
event_notify: mpsc::Sender<()>,
pending_read: Vec<u8>,
read_blocker: Option<futures::sync::oneshot::Sender<Result<(), ()>>>,
read_paused: bool,
need_disconnect: bool,
id: u64,
}
impl Connection {
fn schedule_read(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, us: Arc<Mutex<Self>>, reader: futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>) {
let us_ref = us.clone();
let us_close_ref = us.clone();
let peer_manager_ref = peer_manager.clone();
tokio::spawn(reader.for_each(move |b| {
let pending_read = b.to_vec();
{
let mut lock = us_ref.lock().unwrap();
assert!(lock.pending_read.is_empty());
if lock.read_paused {
lock.pending_read = pending_read;
let (sender, blocker) = futures::sync::oneshot::channel();
lock.read_blocker = Some(sender);
return future::Either::A(blocker.then(|_| { Ok(()) }));
}
}
//TODO: There's a race where we don't meet the requirements of disconnect_socket if its
//called right here, after we release the us_ref lock in the scope above, but before we
//call read_event!
match peer_manager.read_event(&mut SocketDescriptor::new(us_ref.clone(), peer_manager.clone()), pending_read) {
Ok(pause_read) => {
if pause_read {
let mut lock = us_ref.lock().unwrap();
lock.read_paused = true;
}
},
Err(e) => {
us_ref.lock().unwrap().need_disconnect = false;
return future::Either::B(future::result(Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e))));
}
}

if let Err(e) = us_ref.lock().unwrap().event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}

future::Either::B(future::result(Ok(())))
}).then(move |_| {
if us_close_ref.lock().unwrap().need_disconnect {
peer_manager_ref.disconnect_event(&SocketDescriptor::new(us_close_ref, peer_manager_ref.clone()));
println!("Peer disconnected!");
} else {
println!("We disconnected peer!");
}
Ok(())
}));
}

fn new(event_notify: mpsc::Sender<()>, stream: TcpStream) -> (futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>, Arc<Mutex<Self>>) {
let (writer, reader) = tokio_codec::Framed::new(stream, tokio_codec::BytesCodec::new()).split();
let (send_sink, send_stream) = mpsc::channel(3);
tokio::spawn(writer.send_all(send_stream.map_err(|_| -> std::io::Error {
unreachable!();
})).then(|_| {
future::result(Ok(()))
}));
let us = Arc::new(Mutex::new(Self { writer: Some(send_sink), event_notify, pending_read: Vec::new(), read_blocker: None, read_paused: false, need_disconnect: true, id: ID_COUNTER.fetch_add(1, Ordering::AcqRel) }));

(reader, us)
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// accepting an incoming connection (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_inbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(_) = peer_manager.new_inbound_connection(SocketDescriptor::new(us.clone(), peer_manager.clone())) {
Self::schedule_read(peer_manager, us, reader);
}
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// making an outbound connection which is expected to be accepted by a peer with the given
/// public key (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(initial_send) = peer_manager.new_outbound_connection(their_node_id, SocketDescriptor::new(us.clone(), peer_manager.clone())) {
if SocketDescriptor::new(us.clone(), peer_manager.clone()).send_data(&initial_send, 0, true) == initial_send.len() {
Self::schedule_read(peer_manager, us, reader);
} else {
println!("Failed to write first full message to socket!");
}
}
}

/// Process incoming messages and feed outgoing messages on a new connection made to the given
/// socket address which is expected to be accepted by a peer with the given public key (by
/// scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn connect_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, addr: SocketAddr) {
let connect_timeout = Delay::new(Instant::now() + Duration::from_secs(10)).then(|_| {
future::err(std::io::Error::new(std::io::ErrorKind::TimedOut, "timeout reached"))
});
tokio::spawn(TcpStream::connect(&addr).select(connect_timeout)
.and_then(move |stream| {
Connection::setup_outbound(peer_manager, event_notify, their_node_id, stream.0);
future::ok(())
}).or_else(|_| {
//TODO: return errors somehow
future::ok(())
}));
}
}

#[derive(Clone)]
pub struct SocketDescriptor {
conn: Arc<Mutex<Connection>>,
id: u64,
peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>,
}
impl SocketDescriptor {
fn new(conn: Arc<Mutex<Connection>>, peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>) -> Self {
let id = conn.lock().unwrap().id;
Self { conn, id, peer_manager }
}
}
impl peer_handler::SocketDescriptor for SocketDescriptor {
fn send_data(&mut self, data: &Vec<u8>, write_offset: usize, resume_read: bool) -> usize {
macro_rules! schedule_read {
($us_ref: expr) => {
tokio::spawn(future::lazy(move || -> Result<(), ()> {
let mut read_data = Vec::new();
{
let mut us = $us_ref.conn.lock().unwrap();
mem::swap(&mut read_data, &mut us.pending_read);
}
if !read_data.is_empty() {
let mut us_clone = $us_ref.clone();
match $us_ref.peer_manager.read_event(&mut us_clone, read_data) {
Ok(pause_read) => {
if pause_read { return Ok(()); }
},
Err(_) => {
//TODO: Not actually sure how to do this
return Ok(());
}
}
}
let mut us = $us_ref.conn.lock().unwrap();
if let Some(sender) = us.read_blocker.take() {
sender.send(Ok(())).unwrap();
}
us.read_paused = false;
if let Err(e) = us.event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}
Ok(())
}));
}
}

let mut us = self.conn.lock().unwrap();
if resume_read {
let us_ref = self.clone();
schedule_read!(us_ref);
}
if data.len() == write_offset { return 0; }
if us.writer.is_none() {
us.read_paused = true;
return 0;
}

let mut bytes = bytes::BytesMut::with_capacity(data.len() - write_offset);
bytes.put(&data[write_offset..]);
let write_res = us.writer.as_mut().unwrap().start_send(bytes.freeze());
match write_res {
Ok(res) => {
match res {
AsyncSink::Ready => {
data.len() - write_offset
},
AsyncSink::NotReady(_) => {
us.read_paused = true;
let us_ref = self.clone();
tokio::spawn(us.writer.take().unwrap().flush().then(move |writer_res| -> Result<(), ()> {
if let Ok(writer) = writer_res {
{
let mut us = us_ref.conn.lock().unwrap();
us.writer = Some(writer);
}
schedule_read!(us_ref);
} // we'll fire the disconnect event on the socket reader end
Ok(())
}));
0
}
}
},
Err(_) => {
// We'll fire the disconnected event on the socket reader end
0
},
}
}

fn disconnect_socket(&mut self) {
let mut us = self.conn.lock().unwrap();
us.need_disconnect = true;
us.read_paused = true;
}
}
impl Eq for SocketDescriptor {}
impl PartialEq for SocketDescriptor {
fn eq(&self, o: &Self) -> bool {
self.id == o.id
}
}
impl Hash for SocketDescriptor {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,3 +14,4 @@ script:
- cargo build --verbose
- cargo test --verbose
- if [ "$(rustup show | grep default | grep 1.29.2)" != "" ]; then cd fuzz && cargo test --verbose && ./travis-fuzz.sh; fi
- if [ "$(rustup show | grep default | grep stable)" != "" ]; then cd net-tokio && cargo build --verbose; fi
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
[package]
name = "lightning"
version = "0.0.8"
version = "0.0.9"
authors = ["Matt Corallo"]
license = "Apache-2.0"
repository = "https://github.com/rust-bitcoin/rust-lightning/"
Expand All@@ -22,13 +22,13 @@ max_level_info = []
max_level_debug = []

[dependencies]
bitcoin = "0.17"
bitcoin = "0.18"
bitcoin_hashes = "0.3"
rand = "0.4"
secp256k1 = "0.12"

[dev-dependencies.bitcoin]
version = "0.17"
version = "0.18"
features = ["bitcoinconsensus"]

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion fuzz/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ libfuzzer_fuzz = ["libfuzzer-sys"]
[dependencies]
afl = { version = "0.4", optional = true }
lightning = { path = "..", features = ["fuzztarget"] }
bitcoin = { version = "0.17", features = ["fuzztarget"] }
bitcoin = { version = "0.18", features = ["fuzztarget"] }
bitcoin_hashes = { version = "0.3", features=["fuzztarget"] }
hex = "0.3"
honggfuzz = { version = "0.5", optional = true }
Expand Down
19 changes: 19 additions & 0 deletions net-tokio/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
[package]
name = "lightning-net-tokio"
version = "0.0.1"
authors = ["Matt Corallo"]
license = "Apache-2.0"
description = """
Implementation of the rust-lightning network stack using Tokio.
For Rust-Lightning clients which wish to make direct connections to Lightning P2P nodes, this is a simple alternative to implementing the nerequired network stack, especially for those already using Tokio.
"""

[dependencies]
bitcoin = "0.18"
bitcoin_hashes = "0.3"
lightning = { version = "0.0.9", path = "../" }
secp256k1 = "0.12"
tokio-codec = "0.1"
futures = "0.1"
tokio = "0.1"
bytes = "0.4"
270 changes: 270 additions & 0 deletions net-tokio/src/lib.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
extern crate bytes;
extern crate tokio;
extern crate tokio_codec;
extern crate futures;
extern crate lightning;
extern crate secp256k1;

use bytes::BufMut;

use futures::future;
use futures::future::Future;
use futures::{AsyncSink, Stream, Sink};
use futures::sync::mpsc;

use secp256k1::key::PublicKey;

use tokio::timer::Delay;
use tokio::net::TcpStream;

use lightning::ln::peer_handler;
use lightning::ln::peer_handler::SocketDescriptor as LnSocketTrait;

use std::mem;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
use std::vec::Vec;
use std::hash::Hash;

static ID_COUNTER: AtomicU64 = AtomicU64::new(0);

/// A connection to a remote peer. Can be constructed either as a remote connection using
/// Connection::setup_outbound o
pub struct Connection {
writer: Option<mpsc::Sender<bytes::Bytes>>,
event_notify: mpsc::Sender<()>,
pending_read: Vec<u8>,
read_blocker: Option<futures::sync::oneshot::Sender<Result<(), ()>>>,
read_paused: bool,
need_disconnect: bool,
id: u64,
}
impl Connection {
fn schedule_read(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, us: Arc<Mutex<Self>>, reader: futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>) {
let us_ref = us.clone();
let us_close_ref = us.clone();
let peer_manager_ref = peer_manager.clone();
tokio::spawn(reader.for_each(move |b| {
let pending_read = b.to_vec();
{
let mut lock = us_ref.lock().unwrap();
assert!(lock.pending_read.is_empty());
if lock.read_paused {
lock.pending_read = pending_read;
let (sender, blocker) = futures::sync::oneshot::channel();
lock.read_blocker = Some(sender);
return future::Either::A(blocker.then(|_| { Ok(()) }));
}
}
//TODO: There's a race where we don't meet the requirements of disconnect_socket if its
//called right here, after we release the us_ref lock in the scope above, but before we
//call read_event!
match peer_manager.read_event(&mut SocketDescriptor::new(us_ref.clone(), peer_manager.clone()), pending_read) {
Ok(pause_read) => {
if pause_read {
let mut lock = us_ref.lock().unwrap();
lock.read_paused = true;
}
},
Err(e) => {
us_ref.lock().unwrap().need_disconnect = false;
return future::Either::B(future::result(Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e))));
}
}

if let Err(e) = us_ref.lock().unwrap().event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}

future::Either::B(future::result(Ok(())))
}).then(move |_| {
if us_close_ref.lock().unwrap().need_disconnect {
peer_manager_ref.disconnect_event(&SocketDescriptor::new(us_close_ref, peer_manager_ref.clone()));
println!("Peer disconnected!");
} else {
println!("We disconnected peer!");
}
Ok(())
}));
}

fn new(event_notify: mpsc::Sender<()>, stream: TcpStream) -> (futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>, Arc<Mutex<Self>>) {
let (writer, reader) = tokio_codec::Framed::new(stream, tokio_codec::BytesCodec::new()).split();
let (send_sink, send_stream) = mpsc::channel(3);
tokio::spawn(writer.send_all(send_stream.map_err(|_| -> std::io::Error {
unreachable!();
})).then(|_| {
future::result(Ok(()))
}));
let us = Arc::new(Mutex::new(Self { writer: Some(send_sink), event_notify, pending_read: Vec::new(), read_blocker: None, read_paused: false, need_disconnect: true, id: ID_COUNTER.fetch_add(1, Ordering::AcqRel) }));

(reader, us)
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// accepting an incoming connection (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_inbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(_) = peer_manager.new_inbound_connection(SocketDescriptor::new(us.clone(), peer_manager.clone())) {
Self::schedule_read(peer_manager, us, reader);
}
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// making an outbound connection which is expected to be accepted by a peer with the given
/// public key (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(initial_send) = peer_manager.new_outbound_connection(their_node_id, SocketDescriptor::new(us.clone(), peer_manager.clone())) {
if SocketDescriptor::new(us.clone(), peer_manager.clone()).send_data(&initial_send, 0, true) == initial_send.len() {
Self::schedule_read(peer_manager, us, reader);
} else {
println!("Failed to write first full message to socket!");
}
}
}

/// Process incoming messages and feed outgoing messages on a new connection made to the given
/// socket address which is expected to be accepted by a peer with the given public key (by
/// scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn connect_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, addr: SocketAddr) {
let connect_timeout = Delay::new(Instant::now() + Duration::from_secs(10)).then(|_| {
future::err(std::io::Error::new(std::io::ErrorKind::TimedOut, "timeout reached"))
});
tokio::spawn(TcpStream::connect(&addr).select(connect_timeout)
.and_then(move |stream| {
Connection::setup_outbound(peer_manager, event_notify, their_node_id, stream.0);
future::ok(())
}).or_else(|_| {
//TODO: return errors somehow
future::ok(())
}));
}
}

#[derive(Clone)]
pub struct SocketDescriptor {
conn: Arc<Mutex<Connection>>,
id: u64,
peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>,
}
impl SocketDescriptor {
fn new(conn: Arc<Mutex<Connection>>, peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>) -> Self {
let id = conn.lock().unwrap().id;
Self { conn, id, peer_manager }
}
}
impl peer_handler::SocketDescriptor for SocketDescriptor {
fn send_data(&mut self, data: &Vec<u8>, write_offset: usize, resume_read: bool) -> usize {
macro_rules! schedule_read {
($us_ref: expr) => {
tokio::spawn(future::lazy(move || -> Result<(), ()> {
let mut read_data = Vec::new();
{
let mut us = $us_ref.conn.lock().unwrap();
mem::swap(&mut read_data, &mut us.pending_read);
}
if !read_data.is_empty() {
let mut us_clone = $us_ref.clone();
match $us_ref.peer_manager.read_event(&mut us_clone, read_data) {
Ok(pause_read) => {
if pause_read { return Ok(()); }
},
Err(_) => {
//TODO: Not actually sure how to do this
return Ok(());
}
}
}
let mut us = $us_ref.conn.lock().unwrap();
if let Some(sender) = us.read_blocker.take() {
sender.send(Ok(())).unwrap();
}
us.read_paused = false;
if let Err(e) = us.event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}
Ok(())
}));
}
}

let mut us = self.conn.lock().unwrap();
if resume_read {
let us_ref = self.clone();
schedule_read!(us_ref);
}
if data.len() == write_offset { return 0; }
if us.writer.is_none() {
us.read_paused = true;
return 0;
}

let mut bytes = bytes::BytesMut::with_capacity(data.len() - write_offset);
bytes.put(&data[write_offset..]);
let write_res = us.writer.as_mut().unwrap().start_send(bytes.freeze());
match write_res {
Ok(res) => {
match res {
AsyncSink::Ready => {
data.len() - write_offset
},
AsyncSink::NotReady(_) => {
us.read_paused = true;
let us_ref = self.clone();
tokio::spawn(us.writer.take().unwrap().flush().then(move |writer_res| -> Result<(), ()> {
if let Ok(writer) = writer_res {
{
let mut us = us_ref.conn.lock().unwrap();
us.writer = Some(writer);
}
schedule_read!(us_ref);
} // we'll fire the disconnect event on the socket reader end
Ok(())
}));
0
}
}
},
Err(_) => {
// We'll fire the disconnected event on the socket reader end
0
},
}
}

fn disconnect_socket(&mut self) {
let mut us = self.conn.lock().unwrap();
us.need_disconnect = true;
us.read_paused = true;
}
}
impl Eq for SocketDescriptor {}
impl PartialEq for SocketDescriptor {
fn eq(&self, o: &Self) -> bool {
self.id == o.id
}
}
impl Hash for SocketDescriptor {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,3 +14,4 @@ script:
- cargo build --verbose
- cargo test --verbose
- if [ "$(rustup show | grep default | grep 1.29.2)" != "" ]; then cd fuzz && cargo test --verbose && ./travis-fuzz.sh; fi
- if [ "$(rustup show | grep default | grep stable)" != "" ]; then cd net-tokio && cargo build --verbose; fi
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
[package]
name = "lightning"
version = "0.0.8"
version = "0.0.9"
authors = ["Matt Corallo"]
license = "Apache-2.0"
repository = "https://github.com/rust-bitcoin/rust-lightning/"
Expand All@@ -22,13 +22,13 @@ max_level_info = []
max_level_debug = []

[dependencies]
bitcoin = "0.17"
bitcoin = "0.18"
bitcoin_hashes = "0.3"
rand = "0.4"
secp256k1 = "0.12"

[dev-dependencies.bitcoin]
version = "0.17"
version = "0.18"
features = ["bitcoinconsensus"]

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion fuzz/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ libfuzzer_fuzz = ["libfuzzer-sys"]
[dependencies]
afl = { version = "0.4", optional = true }
lightning = { path = "..", features = ["fuzztarget"] }
bitcoin = { version = "0.17", features = ["fuzztarget"] }
bitcoin = { version = "0.18", features = ["fuzztarget"] }
bitcoin_hashes = { version = "0.3", features=["fuzztarget"] }
hex = "0.3"
honggfuzz = { version = "0.5", optional = true }
Expand Down
19 changes: 19 additions & 0 deletions net-tokio/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
[package]
name = "lightning-net-tokio"
version = "0.0.1"
authors = ["Matt Corallo"]
license = "Apache-2.0"
description = """
Implementation of the rust-lightning network stack using Tokio.
For Rust-Lightning clients which wish to make direct connections to Lightning P2P nodes, this is a simple alternative to implementing the nerequired network stack, especially for those already using Tokio.
"""

[dependencies]
bitcoin = "0.18"
bitcoin_hashes = "0.3"
lightning = { version = "0.0.9", path = "../" }
secp256k1 = "0.12"
tokio-codec = "0.1"
futures = "0.1"
tokio = "0.1"
bytes = "0.4"
270 changes: 270 additions & 0 deletions net-tokio/src/lib.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
extern crate bytes;
extern crate tokio;
extern crate tokio_codec;
extern crate futures;
extern crate lightning;
extern crate secp256k1;

use bytes::BufMut;

use futures::future;
use futures::future::Future;
use futures::{AsyncSink, Stream, Sink};
use futures::sync::mpsc;

use secp256k1::key::PublicKey;

use tokio::timer::Delay;
use tokio::net::TcpStream;

use lightning::ln::peer_handler;
use lightning::ln::peer_handler::SocketDescriptor as LnSocketTrait;

use std::mem;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
use std::vec::Vec;
use std::hash::Hash;

static ID_COUNTER: AtomicU64 = AtomicU64::new(0);

/// A connection to a remote peer. Can be constructed either as a remote connection using
/// Connection::setup_outbound o
pub struct Connection {
writer: Option<mpsc::Sender<bytes::Bytes>>,
event_notify: mpsc::Sender<()>,
pending_read: Vec<u8>,
read_blocker: Option<futures::sync::oneshot::Sender<Result<(), ()>>>,
read_paused: bool,
need_disconnect: bool,
id: u64,
}
impl Connection {
fn schedule_read(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, us: Arc<Mutex<Self>>, reader: futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>) {
let us_ref = us.clone();
let us_close_ref = us.clone();
let peer_manager_ref = peer_manager.clone();
tokio::spawn(reader.for_each(move |b| {
let pending_read = b.to_vec();
{
let mut lock = us_ref.lock().unwrap();
assert!(lock.pending_read.is_empty());
if lock.read_paused {
lock.pending_read = pending_read;
let (sender, blocker) = futures::sync::oneshot::channel();
lock.read_blocker = Some(sender);
return future::Either::A(blocker.then(|_| { Ok(()) }));
}
}
//TODO: There's a race where we don't meet the requirements of disconnect_socket if its
//called right here, after we release the us_ref lock in the scope above, but before we
//call read_event!
match peer_manager.read_event(&mut SocketDescriptor::new(us_ref.clone(), peer_manager.clone()), pending_read) {
Ok(pause_read) => {
if pause_read {
let mut lock = us_ref.lock().unwrap();
lock.read_paused = true;
}
},
Err(e) => {
us_ref.lock().unwrap().need_disconnect = false;
return future::Either::B(future::result(Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e))));
}
}

if let Err(e) = us_ref.lock().unwrap().event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}

future::Either::B(future::result(Ok(())))
}).then(move |_| {
if us_close_ref.lock().unwrap().need_disconnect {
peer_manager_ref.disconnect_event(&SocketDescriptor::new(us_close_ref, peer_manager_ref.clone()));
println!("Peer disconnected!");
} else {
println!("We disconnected peer!");
}
Ok(())
}));
}

fn new(event_notify: mpsc::Sender<()>, stream: TcpStream) -> (futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>, Arc<Mutex<Self>>) {
let (writer, reader) = tokio_codec::Framed::new(stream, tokio_codec::BytesCodec::new()).split();
let (send_sink, send_stream) = mpsc::channel(3);
tokio::spawn(writer.send_all(send_stream.map_err(|_| -> std::io::Error {
unreachable!();
})).then(|_| {
future::result(Ok(()))
}));
let us = Arc::new(Mutex::new(Self { writer: Some(send_sink), event_notify, pending_read: Vec::new(), read_blocker: None, read_paused: false, need_disconnect: true, id: ID_COUNTER.fetch_add(1, Ordering::AcqRel) }));

(reader, us)
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// accepting an incoming connection (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_inbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(_) = peer_manager.new_inbound_connection(SocketDescriptor::new(us.clone(), peer_manager.clone())) {
Self::schedule_read(peer_manager, us, reader);
}
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// making an outbound connection which is expected to be accepted by a peer with the given
/// public key (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(initial_send) = peer_manager.new_outbound_connection(their_node_id, SocketDescriptor::new(us.clone(), peer_manager.clone())) {
if SocketDescriptor::new(us.clone(), peer_manager.clone()).send_data(&initial_send, 0, true) == initial_send.len() {
Self::schedule_read(peer_manager, us, reader);
} else {
println!("Failed to write first full message to socket!");
}
}
}

/// Process incoming messages and feed outgoing messages on a new connection made to the given
/// socket address which is expected to be accepted by a peer with the given public key (by
/// scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn connect_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, addr: SocketAddr) {
let connect_timeout = Delay::new(Instant::now() + Duration::from_secs(10)).then(|_| {
future::err(std::io::Error::new(std::io::ErrorKind::TimedOut, "timeout reached"))
});
tokio::spawn(TcpStream::connect(&addr).select(connect_timeout)
.and_then(move |stream| {
Connection::setup_outbound(peer_manager, event_notify, their_node_id, stream.0);
future::ok(())
}).or_else(|_| {
//TODO: return errors somehow
future::ok(())
}));
}
}

#[derive(Clone)]
pub struct SocketDescriptor {
conn: Arc<Mutex<Connection>>,
id: u64,
peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>,
}
impl SocketDescriptor {
fn new(conn: Arc<Mutex<Connection>>, peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>) -> Self {
let id = conn.lock().unwrap().id;
Self { conn, id, peer_manager }
}
}
impl peer_handler::SocketDescriptor for SocketDescriptor {
fn send_data(&mut self, data: &Vec<u8>, write_offset: usize, resume_read: bool) -> usize {
macro_rules! schedule_read {
($us_ref: expr) => {
tokio::spawn(future::lazy(move || -> Result<(), ()> {
let mut read_data = Vec::new();
{
let mut us = $us_ref.conn.lock().unwrap();
mem::swap(&mut read_data, &mut us.pending_read);
}
if !read_data.is_empty() {
let mut us_clone = $us_ref.clone();
match $us_ref.peer_manager.read_event(&mut us_clone, read_data) {
Ok(pause_read) => {
if pause_read { return Ok(()); }
},
Err(_) => {
//TODO: Not actually sure how to do this
return Ok(());
}
}
}
let mut us = $us_ref.conn.lock().unwrap();
if let Some(sender) = us.read_blocker.take() {
sender.send(Ok(())).unwrap();
}
us.read_paused = false;
if let Err(e) = us.event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}
Ok(())
}));
}
}

let mut us = self.conn.lock().unwrap();
if resume_read {
let us_ref = self.clone();
schedule_read!(us_ref);
}
if data.len() == write_offset { return 0; }
if us.writer.is_none() {
us.read_paused = true;
return 0;
}

let mut bytes = bytes::BytesMut::with_capacity(data.len() - write_offset);
bytes.put(&data[write_offset..]);
let write_res = us.writer.as_mut().unwrap().start_send(bytes.freeze());
match write_res {
Ok(res) => {
match res {
AsyncSink::Ready => {
data.len() - write_offset
},
AsyncSink::NotReady(_) => {
us.read_paused = true;
let us_ref = self.clone();
tokio::spawn(us.writer.take().unwrap().flush().then(move |writer_res| -> Result<(), ()> {
if let Ok(writer) = writer_res {
{
let mut us = us_ref.conn.lock().unwrap();
us.writer = Some(writer);
}
schedule_read!(us_ref);
} // we'll fire the disconnect event on the socket reader end
Ok(())
}));
0
}
}
},
Err(_) => {
// We'll fire the disconnected event on the socket reader end
0
},
}
}

fn disconnect_socket(&mut self) {
let mut us = self.conn.lock().unwrap();
us.need_disconnect = true;
us.read_paused = true;
}
}
impl Eq for SocketDescriptor {}
impl PartialEq for SocketDescriptor {
fn eq(&self, o: &Self) -> bool {
self.id == o.id
}
}
impl Hash for SocketDescriptor {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,3 +14,4 @@ script:
- cargo build --verbose
- cargo test --verbose
- if [ "$(rustup show | grep default | grep 1.29.2)" != "" ]; then cd fuzz && cargo test --verbose && ./travis-fuzz.sh; fi
- if [ "$(rustup show | grep default | grep stable)" != "" ]; then cd net-tokio && cargo build --verbose; fi
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
[package]
name = "lightning"
version = "0.0.8"
version = "0.0.9"
authors = ["Matt Corallo"]
license = "Apache-2.0"
repository = "https://github.com/rust-bitcoin/rust-lightning/"
Expand All@@ -22,13 +22,13 @@ max_level_info = []
max_level_debug = []

[dependencies]
bitcoin = "0.17"
bitcoin = "0.18"
bitcoin_hashes = "0.3"
rand = "0.4"
secp256k1 = "0.12"

[dev-dependencies.bitcoin]
version = "0.17"
version = "0.18"
features = ["bitcoinconsensus"]

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion fuzz/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ libfuzzer_fuzz = ["libfuzzer-sys"]
[dependencies]
afl = { version = "0.4", optional = true }
lightning = { path = "..", features = ["fuzztarget"] }
bitcoin = { version = "0.17", features = ["fuzztarget"] }
bitcoin = { version = "0.18", features = ["fuzztarget"] }
bitcoin_hashes = { version = "0.3", features=["fuzztarget"] }
hex = "0.3"
honggfuzz = { version = "0.5", optional = true }
Expand Down
19 changes: 19 additions & 0 deletions net-tokio/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
[package]
name = "lightning-net-tokio"
version = "0.0.1"
authors = ["Matt Corallo"]
license = "Apache-2.0"
description = """
Implementation of the rust-lightning network stack using Tokio.
For Rust-Lightning clients which wish to make direct connections to Lightning P2P nodes, this is a simple alternative to implementing the nerequired network stack, especially for those already using Tokio.
"""

[dependencies]
bitcoin = "0.18"
bitcoin_hashes = "0.3"
lightning = { version = "0.0.9", path = "../" }
secp256k1 = "0.12"
tokio-codec = "0.1"
futures = "0.1"
tokio = "0.1"
bytes = "0.4"
270 changes: 270 additions & 0 deletions net-tokio/src/lib.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
extern crate bytes;
extern crate tokio;
extern crate tokio_codec;
extern crate futures;
extern crate lightning;
extern crate secp256k1;

use bytes::BufMut;

use futures::future;
use futures::future::Future;
use futures::{AsyncSink, Stream, Sink};
use futures::sync::mpsc;

use secp256k1::key::PublicKey;

use tokio::timer::Delay;
use tokio::net::TcpStream;

use lightning::ln::peer_handler;
use lightning::ln::peer_handler::SocketDescriptor as LnSocketTrait;

use std::mem;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
use std::vec::Vec;
use std::hash::Hash;

static ID_COUNTER: AtomicU64 = AtomicU64::new(0);

/// A connection to a remote peer. Can be constructed either as a remote connection using
/// Connection::setup_outbound o
pub struct Connection {
writer: Option<mpsc::Sender<bytes::Bytes>>,
event_notify: mpsc::Sender<()>,
pending_read: Vec<u8>,
read_blocker: Option<futures::sync::oneshot::Sender<Result<(), ()>>>,
read_paused: bool,
need_disconnect: bool,
id: u64,
}
impl Connection {
fn schedule_read(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, us: Arc<Mutex<Self>>, reader: futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>) {
let us_ref = us.clone();
let us_close_ref = us.clone();
let peer_manager_ref = peer_manager.clone();
tokio::spawn(reader.for_each(move |b| {
let pending_read = b.to_vec();
{
let mut lock = us_ref.lock().unwrap();
assert!(lock.pending_read.is_empty());
if lock.read_paused {
lock.pending_read = pending_read;
let (sender, blocker) = futures::sync::oneshot::channel();
lock.read_blocker = Some(sender);
return future::Either::A(blocker.then(|_| { Ok(()) }));
}
}
//TODO: There's a race where we don't meet the requirements of disconnect_socket if its
//called right here, after we release the us_ref lock in the scope above, but before we
//call read_event!
match peer_manager.read_event(&mut SocketDescriptor::new(us_ref.clone(), peer_manager.clone()), pending_read) {
Ok(pause_read) => {
if pause_read {
let mut lock = us_ref.lock().unwrap();
lock.read_paused = true;
}
},
Err(e) => {
us_ref.lock().unwrap().need_disconnect = false;
return future::Either::B(future::result(Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e))));
}
}

if let Err(e) = us_ref.lock().unwrap().event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}

future::Either::B(future::result(Ok(())))
}).then(move |_| {
if us_close_ref.lock().unwrap().need_disconnect {
peer_manager_ref.disconnect_event(&SocketDescriptor::new(us_close_ref, peer_manager_ref.clone()));
println!("Peer disconnected!");
} else {
println!("We disconnected peer!");
}
Ok(())
}));
}

fn new(event_notify: mpsc::Sender<()>, stream: TcpStream) -> (futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>, Arc<Mutex<Self>>) {
let (writer, reader) = tokio_codec::Framed::new(stream, tokio_codec::BytesCodec::new()).split();
let (send_sink, send_stream) = mpsc::channel(3);
tokio::spawn(writer.send_all(send_stream.map_err(|_| -> std::io::Error {
unreachable!();
})).then(|_| {
future::result(Ok(()))
}));
let us = Arc::new(Mutex::new(Self { writer: Some(send_sink), event_notify, pending_read: Vec::new(), read_blocker: None, read_paused: false, need_disconnect: true, id: ID_COUNTER.fetch_add(1, Ordering::AcqRel) }));

(reader, us)
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// accepting an incoming connection (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_inbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(_) = peer_manager.new_inbound_connection(SocketDescriptor::new(us.clone(), peer_manager.clone())) {
Self::schedule_read(peer_manager, us, reader);
}
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// making an outbound connection which is expected to be accepted by a peer with the given
/// public key (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(initial_send) = peer_manager.new_outbound_connection(their_node_id, SocketDescriptor::new(us.clone(), peer_manager.clone())) {
if SocketDescriptor::new(us.clone(), peer_manager.clone()).send_data(&initial_send, 0, true) == initial_send.len() {
Self::schedule_read(peer_manager, us, reader);
} else {
println!("Failed to write first full message to socket!");
}
}
}

/// Process incoming messages and feed outgoing messages on a new connection made to the given
/// socket address which is expected to be accepted by a peer with the given public key (by
/// scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn connect_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, addr: SocketAddr) {
let connect_timeout = Delay::new(Instant::now() + Duration::from_secs(10)).then(|_| {
future::err(std::io::Error::new(std::io::ErrorKind::TimedOut, "timeout reached"))
});
tokio::spawn(TcpStream::connect(&addr).select(connect_timeout)
.and_then(move |stream| {
Connection::setup_outbound(peer_manager, event_notify, their_node_id, stream.0);
future::ok(())
}).or_else(|_| {
//TODO: return errors somehow
future::ok(())
}));
}
}

#[derive(Clone)]
pub struct SocketDescriptor {
conn: Arc<Mutex<Connection>>,
id: u64,
peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>,
}
impl SocketDescriptor {
fn new(conn: Arc<Mutex<Connection>>, peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>) -> Self {
let id = conn.lock().unwrap().id;
Self { conn, id, peer_manager }
}
}
impl peer_handler::SocketDescriptor for SocketDescriptor {
fn send_data(&mut self, data: &Vec<u8>, write_offset: usize, resume_read: bool) -> usize {
macro_rules! schedule_read {
($us_ref: expr) => {
tokio::spawn(future::lazy(move || -> Result<(), ()> {
let mut read_data = Vec::new();
{
let mut us = $us_ref.conn.lock().unwrap();
mem::swap(&mut read_data, &mut us.pending_read);
}
if !read_data.is_empty() {
let mut us_clone = $us_ref.clone();
match $us_ref.peer_manager.read_event(&mut us_clone, read_data) {
Ok(pause_read) => {
if pause_read { return Ok(()); }
},
Err(_) => {
//TODO: Not actually sure how to do this
return Ok(());
}
}
}
let mut us = $us_ref.conn.lock().unwrap();
if let Some(sender) = us.read_blocker.take() {
sender.send(Ok(())).unwrap();
}
us.read_paused = false;
if let Err(e) = us.event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}
Ok(())
}));
}
}

let mut us = self.conn.lock().unwrap();
if resume_read {
let us_ref = self.clone();
schedule_read!(us_ref);
}
if data.len() == write_offset { return 0; }
if us.writer.is_none() {
us.read_paused = true;
return 0;
}

let mut bytes = bytes::BytesMut::with_capacity(data.len() - write_offset);
bytes.put(&data[write_offset..]);
let write_res = us.writer.as_mut().unwrap().start_send(bytes.freeze());
match write_res {
Ok(res) => {
match res {
AsyncSink::Ready => {
data.len() - write_offset
},
AsyncSink::NotReady(_) => {
us.read_paused = true;
let us_ref = self.clone();
tokio::spawn(us.writer.take().unwrap().flush().then(move |writer_res| -> Result<(), ()> {
if let Ok(writer) = writer_res {
{
let mut us = us_ref.conn.lock().unwrap();
us.writer = Some(writer);
}
schedule_read!(us_ref);
} // we'll fire the disconnect event on the socket reader end
Ok(())
}));
0
}
}
},
Err(_) => {
// We'll fire the disconnected event on the socket reader end
0
},
}
}

fn disconnect_socket(&mut self) {
let mut us = self.conn.lock().unwrap();
us.need_disconnect = true;
us.read_paused = true;
}
}
impl Eq for SocketDescriptor {}
impl PartialEq for SocketDescriptor {
fn eq(&self, o: &Self) -> bool {
self.id == o.id
}
}
impl Hash for SocketDescriptor {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,3 +14,4 @@ script:
- cargo build --verbose
- cargo test --verbose
- if [ "$(rustup show | grep default | grep 1.29.2)" != "" ]; then cd fuzz && cargo test --verbose && ./travis-fuzz.sh; fi
- if [ "$(rustup show | grep default | grep stable)" != "" ]; then cd net-tokio && cargo build --verbose; fi
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
[package]
name = "lightning"
version = "0.0.8"
version = "0.0.9"
authors = ["Matt Corallo"]
license = "Apache-2.0"
repository = "https://github.com/rust-bitcoin/rust-lightning/"
Expand All@@ -22,13 +22,13 @@ max_level_info = []
max_level_debug = []

[dependencies]
bitcoin = "0.17"
bitcoin = "0.18"
bitcoin_hashes = "0.3"
rand = "0.4"
secp256k1 = "0.12"

[dev-dependencies.bitcoin]
version = "0.17"
version = "0.18"
features = ["bitcoinconsensus"]

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion fuzz/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ libfuzzer_fuzz = ["libfuzzer-sys"]
[dependencies]
afl = { version = "0.4", optional = true }
lightning = { path = "..", features = ["fuzztarget"] }
bitcoin = { version = "0.17", features = ["fuzztarget"] }
bitcoin = { version = "0.18", features = ["fuzztarget"] }
bitcoin_hashes = { version = "0.3", features=["fuzztarget"] }
hex = "0.3"
honggfuzz = { version = "0.5", optional = true }
Expand Down
19 changes: 19 additions & 0 deletions net-tokio/Cargo.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
[package]
name = "lightning-net-tokio"
version = "0.0.1"
authors = ["Matt Corallo"]
license = "Apache-2.0"
description = """
Implementation of the rust-lightning network stack using Tokio.
For Rust-Lightning clients which wish to make direct connections to Lightning P2P nodes, this is a simple alternative to implementing the nerequired network stack, especially for those already using Tokio.
"""

[dependencies]
bitcoin = "0.18"
bitcoin_hashes = "0.3"
lightning = { version = "0.0.9", path = "../" }
secp256k1 = "0.12"
tokio-codec = "0.1"
futures = "0.1"
tokio = "0.1"
bytes = "0.4"
270 changes: 270 additions & 0 deletions net-tokio/src/lib.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
extern crate bytes;
extern crate tokio;
extern crate tokio_codec;
extern crate futures;
extern crate lightning;
extern crate secp256k1;

use bytes::BufMut;

use futures::future;
use futures::future::Future;
use futures::{AsyncSink, Stream, Sink};
use futures::sync::mpsc;

use secp256k1::key::PublicKey;

use tokio::timer::Delay;
use tokio::net::TcpStream;

use lightning::ln::peer_handler;
use lightning::ln::peer_handler::SocketDescriptor as LnSocketTrait;

use std::mem;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
use std::vec::Vec;
use std::hash::Hash;

static ID_COUNTER: AtomicU64 = AtomicU64::new(0);

/// A connection to a remote peer. Can be constructed either as a remote connection using
/// Connection::setup_outbound o
pub struct Connection {
writer: Option<mpsc::Sender<bytes::Bytes>>,
event_notify: mpsc::Sender<()>,
pending_read: Vec<u8>,
read_blocker: Option<futures::sync::oneshot::Sender<Result<(), ()>>>,
read_paused: bool,
need_disconnect: bool,
id: u64,
}
impl Connection {
fn schedule_read(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, us: Arc<Mutex<Self>>, reader: futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>) {
let us_ref = us.clone();
let us_close_ref = us.clone();
let peer_manager_ref = peer_manager.clone();
tokio::spawn(reader.for_each(move |b| {
let pending_read = b.to_vec();
{
let mut lock = us_ref.lock().unwrap();
assert!(lock.pending_read.is_empty());
if lock.read_paused {
lock.pending_read = pending_read;
let (sender, blocker) = futures::sync::oneshot::channel();
lock.read_blocker = Some(sender);
return future::Either::A(blocker.then(|_| { Ok(()) }));
}
}
//TODO: There's a race where we don't meet the requirements of disconnect_socket if its
//called right here, after we release the us_ref lock in the scope above, but before we
//call read_event!
match peer_manager.read_event(&mut SocketDescriptor::new(us_ref.clone(), peer_manager.clone()), pending_read) {
Ok(pause_read) => {
if pause_read {
let mut lock = us_ref.lock().unwrap();
lock.read_paused = true;
}
},
Err(e) => {
us_ref.lock().unwrap().need_disconnect = false;
return future::Either::B(future::result(Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e))));
}
}

if let Err(e) = us_ref.lock().unwrap().event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}

future::Either::B(future::result(Ok(())))
}).then(move |_| {
if us_close_ref.lock().unwrap().need_disconnect {
peer_manager_ref.disconnect_event(&SocketDescriptor::new(us_close_ref, peer_manager_ref.clone()));
println!("Peer disconnected!");
} else {
println!("We disconnected peer!");
}
Ok(())
}));
}

fn new(event_notify: mpsc::Sender<()>, stream: TcpStream) -> (futures::stream::SplitStream<tokio_codec::Framed<TcpStream, tokio_codec::BytesCodec>>, Arc<Mutex<Self>>) {
let (writer, reader) = tokio_codec::Framed::new(stream, tokio_codec::BytesCodec::new()).split();
let (send_sink, send_stream) = mpsc::channel(3);
tokio::spawn(writer.send_all(send_stream.map_err(|_| -> std::io::Error {
unreachable!();
})).then(|_| {
future::result(Ok(()))
}));
let us = Arc::new(Mutex::new(Self { writer: Some(send_sink), event_notify, pending_read: Vec::new(), read_blocker: None, read_paused: false, need_disconnect: true, id: ID_COUNTER.fetch_add(1, Ordering::AcqRel) }));

(reader, us)
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// accepting an incoming connection (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_inbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(_) = peer_manager.new_inbound_connection(SocketDescriptor::new(us.clone(), peer_manager.clone())) {
Self::schedule_read(peer_manager, us, reader);
}
}

/// Process incoming messages and feed outgoing messages on the provided socket generated by
/// making an outbound connection which is expected to be accepted by a peer with the given
/// public key (by scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn setup_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, stream: TcpStream) {
let (reader, us) = Self::new(event_notify, stream);

if let Ok(initial_send) = peer_manager.new_outbound_connection(their_node_id, SocketDescriptor::new(us.clone(), peer_manager.clone())) {
if SocketDescriptor::new(us.clone(), peer_manager.clone()).send_data(&initial_send, 0, true) == initial_send.len() {
Self::schedule_read(peer_manager, us, reader);
} else {
println!("Failed to write first full message to socket!");
}
}
}

/// Process incoming messages and feed outgoing messages on a new connection made to the given
/// socket address which is expected to be accepted by a peer with the given public key (by
/// scheduling futures with tokio::spawn).
///
/// You should poll the Receive end of event_notify and call get_and_clear_pending_events() on
/// ChannelManager and ChannelMonitor objects.
pub fn connect_outbound(peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>, event_notify: mpsc::Sender<()>, their_node_id: PublicKey, addr: SocketAddr) {
let connect_timeout = Delay::new(Instant::now() + Duration::from_secs(10)).then(|_| {
future::err(std::io::Error::new(std::io::ErrorKind::TimedOut, "timeout reached"))
});
tokio::spawn(TcpStream::connect(&addr).select(connect_timeout)
.and_then(move |stream| {
Connection::setup_outbound(peer_manager, event_notify, their_node_id, stream.0);
future::ok(())
}).or_else(|_| {
//TODO: return errors somehow
future::ok(())
}));
}
}

#[derive(Clone)]
pub struct SocketDescriptor {
conn: Arc<Mutex<Connection>>,
id: u64,
peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>,
}
impl SocketDescriptor {
fn new(conn: Arc<Mutex<Connection>>, peer_manager: Arc<peer_handler::PeerManager<SocketDescriptor>>) -> Self {
let id = conn.lock().unwrap().id;
Self { conn, id, peer_manager }
}
}
impl peer_handler::SocketDescriptor for SocketDescriptor {
fn send_data(&mut self, data: &Vec<u8>, write_offset: usize, resume_read: bool) -> usize {
macro_rules! schedule_read {
($us_ref: expr) => {
tokio::spawn(future::lazy(move || -> Result<(), ()> {
let mut read_data = Vec::new();
{
let mut us = $us_ref.conn.lock().unwrap();
mem::swap(&mut read_data, &mut us.pending_read);
}
if !read_data.is_empty() {
let mut us_clone = $us_ref.clone();
match $us_ref.peer_manager.read_event(&mut us_clone, read_data) {
Ok(pause_read) => {
if pause_read { return Ok(()); }
},
Err(_) => {
//TODO: Not actually sure how to do this
return Ok(());
}
}
}
let mut us = $us_ref.conn.lock().unwrap();
if let Some(sender) = us.read_blocker.take() {
sender.send(Ok(())).unwrap();
}
us.read_paused = false;
if let Err(e) = us.event_notify.try_send(()) {
// Ignore full errors as we just need them to poll after this point, so if the user
// hasn't received the last send yet, it doesn't matter.
assert!(e.is_full());
}
Ok(())
}));
}
}

let mut us = self.conn.lock().unwrap();
if resume_read {
let us_ref = self.clone();
schedule_read!(us_ref);
}
if data.len() == write_offset { return 0; }
if us.writer.is_none() {
us.read_paused = true;
return 0;
}

let mut bytes = bytes::BytesMut::with_capacity(data.len() - write_offset);
bytes.put(&data[write_offset..]);
let write_res = us.writer.as_mut().unwrap().start_send(bytes.freeze());
match write_res {
Ok(res) => {
match res {
AsyncSink::Ready => {
data.len() - write_offset
},
AsyncSink::NotReady(_) => {
us.read_paused = true;
let us_ref = self.clone();
tokio::spawn(us.writer.take().unwrap().flush().then(move |writer_res| -> Result<(), ()> {
if let Ok(writer) = writer_res {
{
let mut us = us_ref.conn.lock().unwrap();
us.writer = Some(writer);
}
schedule_read!(us_ref);
} // we'll fire the disconnect event on the socket reader end
Ok(())
}));
0
}
}
},
Err(_) => {
// We'll fire the disconnected event on the socket reader end
0
},
}
}

fn disconnect_socket(&mut self) {
let mut us = self.conn.lock().unwrap();
us.need_disconnect = true;
us.read_paused = true;
}
}
impl Eq for SocketDescriptor {}
impl PartialEq for SocketDescriptor {
fn eq(&self, o: &Self) -> bool {
self.id == o.id
}
}
impl Hash for SocketDescriptor {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}