Skip to content

Expand full_stack_target from uselessly-large to laughably-large #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
12 changes: 3 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,13 @@ Assorted random TODOs:
a timer. By default we should provide an implementation of this which uses
some newfangled rusty promise-y library, but should generally ensure a
client can simply integrate this into whatever existing timer interface
they use.

* Networking...having a simple bytes-in-bytes-out interface which does message
handling and calls our encryption layer is probably the right approach. We
should then also probably use the same promise-y library we use for timers
to do socket selection and reading/writing.
they use. (This is partially complete, but the events stuff needs to also
exist in Channel, which has a few inline TODOs to set up timers).

* Figure out how to expose when-to-connect and who-to-connect-to.

* Implement when-to-connect and who-to-connect-to based on route/node rumoring
and channelmanager state.
and channelmanager state (and some concept of available value in wallet).

* Some kind of serialization format for on-disk storage of things like
channels, channelmonitors, routing db, etc.
Expand All @@ -50,8 +46,6 @@ Assorted random TODOs:

* All the random TODOs and unimplemented!()s across the codebase.

* BOLT 11 (invoice/address creation/generation) implementation

* Type-ify our somewhat random usage of Uint256/[u8; 32]. Use Sha256dHash
where appropriate, create our own types for everything else.

Expand Down
1 change: 1 addition & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ honggfuzz_fuzz = ["honggfuzz"]
lightning = { path = "..", features = ["fuzztarget"] }
bitcoin = { git = "https://github.com/rust-bitcoin/rust-bitcoin", features = ["fuzztarget"] }
secp256k1 = { version = "0.9", features = ["fuzztarget"] }
rust-crypto = "0.2"
honggfuzz = { version = "0.5", optional = true }
afl = { version = "0.3", optional = true }

Expand Down
19 changes: 15 additions & 4 deletions fuzz/fuzz_targets/channel_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ extern crate lightning;
extern crate secp256k1;

use bitcoin::blockdata::block::BlockHeader;
use bitcoin::blockdata::transaction::Transaction;
use bitcoin::blockdata::transaction::{Transaction, TxOut};
use bitcoin::util::hash::Sha256dHash;
use bitcoin::network::serialize::{serialize, BitcoinHash};

Expand All @@ -12,6 +12,7 @@ use lightning::ln::channelmanager::PendingForwardHTLCInfo;
use lightning::ln::msgs;
use lightning::ln::msgs::MsgDecodable;
use lightning::chain::chaininterface::{FeeEstimator, ConfirmationTarget};
use lightning::util::reset_rng_state;

use secp256k1::key::PublicKey;
use secp256k1::Secp256k1;
Expand Down Expand Up @@ -89,6 +90,8 @@ impl<'a> FeeEstimator for FuzzEstimator<'a> {

#[inline]
pub fn do_test(data: &[u8]) {
reset_rng_state();

let input = InputData {
data,
read_pos: AtomicUsize::new(0),
Expand Down Expand Up @@ -163,18 +166,22 @@ pub fn do_test(data: &[u8]) {

let their_pubkey = get_pubkey!();

let tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
let funding_output = (Sha256dHash::from_data(&serialize(&tx).unwrap()[..]), 0);
let mut tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };

let mut channel = if get_slice!(1)[0] != 0 {
let mut chan = Channel::new_outbound(&fee_est, their_pubkey, slice_to_be24(get_slice!(3)), get_slice!(1)[0] == 0, slice_to_be64(get_slice!(8)));
let chan_value = slice_to_be24(get_slice!(3));
let mut chan = Channel::new_outbound(&fee_est, their_pubkey, chan_value, get_slice!(1)[0] == 0, slice_to_be64(get_slice!(8)));
chan.get_open_channel(Sha256dHash::from(get_slice!(32)), &fee_est).unwrap();
let accept_chan = if get_slice!(1)[0] == 0 {
decode_msg_with_len16!(msgs::AcceptChannel, 270, 1)
} else {
decode_msg!(msgs::AcceptChannel, 270)
};
return_err!(chan.accept_channel(&accept_chan));

tx.output.push(TxOut{ value: chan_value, script_pubkey: chan.get_funding_redeemscript().to_v0_p2wsh() });
let funding_output = (Sha256dHash::from_data(&serialize(&tx).unwrap()[..]), 0);

chan.get_outbound_funding_created(funding_output.0.clone(), funding_output.1).unwrap();
let funding_signed = decode_msg!(msgs::FundingSigned, 32+64);
return_err!(chan.funding_signed(&funding_signed));
Expand All @@ -190,6 +197,10 @@ pub fn do_test(data: &[u8]) {
Err(_) => return,
};
chan.get_accept_channel().unwrap();

tx.output.push(TxOut{ value: open_chan.funding_satoshis, script_pubkey: chan.get_funding_redeemscript().to_v0_p2wsh() });
let funding_output = (Sha256dHash::from_data(&serialize(&tx).unwrap()[..]), 0);

let mut funding_created = decode_msg!(msgs::FundingCreated, 32+32+2+64);
funding_created.funding_txid = funding_output.0.clone();
funding_created.funding_output_index = funding_output.1;
Expand Down
165 changes: 156 additions & 9 deletions fuzz/fuzz_targets/full_stack_target.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
extern crate bitcoin;
extern crate crypto;
extern crate lightning;
extern crate secp256k1;

use bitcoin::blockdata::transaction::Transaction;
use bitcoin::blockdata::block::BlockHeader;
use bitcoin::blockdata::transaction::{Transaction, TxOut};
use bitcoin::blockdata::script::Script;
use bitcoin::network::constants::Network;
use bitcoin::network::serialize::{serialize, BitcoinHash};
use bitcoin::util::hash::Sha256dHash;
use bitcoin::util::uint::Uint256;

use lightning::chain::chaininterface::{BroadcasterInterface,ConfirmationTarget,FeeEstimator,ChainWatchInterfaceUtil};
use crypto::sha2::Sha256;
use crypto::digest::Digest;

use lightning::chain::chaininterface::{BroadcasterInterface,ConfirmationTarget,ChainListener,FeeEstimator,ChainWatchInterfaceUtil};
use lightning::ln::{channelmonitor,msgs};
use lightning::ln::channelmanager::ChannelManager;
use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor};
use lightning::ln::router::Router;
use lightning::util::events::{EventsProvider,Event};
use lightning::util::reset_rng_state;

use secp256k1::key::{PublicKey,SecretKey};
use secp256k1::Secp256k1;

use std::collections::HashMap;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize,Ordering};

Expand All @@ -24,6 +35,13 @@ pub fn slice_to_be16(v: &[u8]) -> u16 {
((v[1] as u16) << 8*0)
}

#[inline]
pub fn slice_to_be24(v: &[u8]) -> u32 {
((v[0] as u32) << 8*2) |
((v[1] as u32) << 8*1) |
((v[2] as u32) << 8*0)
}

#[inline]
pub fn slice_to_be32(v: &[u8]) -> u32 {
((v[0] as u32) << 8*3) |
Expand All @@ -32,6 +50,20 @@ pub fn slice_to_be32(v: &[u8]) -> u32 {
((v[3] as u32) << 8*0)
}

#[inline]
pub fn be64_to_array(u: u64) -> [u8; 8] {
let mut v = [0; 8];
v[0] = ((u >> 8*7) & 0xff) as u8;
v[1] = ((u >> 8*6) & 0xff) as u8;
v[2] = ((u >> 8*5) & 0xff) as u8;
v[3] = ((u >> 8*4) & 0xff) as u8;
v[4] = ((u >> 8*3) & 0xff) as u8;
v[5] = ((u >> 8*2) & 0xff) as u8;
v[6] = ((u >> 8*1) & 0xff) as u8;
v[7] = ((u >> 8*0) & 0xff) as u8;
v
}

struct InputData {
data: Vec<u8>,
read_pos: AtomicUsize,
Expand All @@ -44,13 +76,6 @@ impl InputData {
}
Some(&self.data[old_pos..old_pos + len])
}
fn get_slice_nonadvancing(&self, len: usize) -> Option<&[u8]> {
let old_pos = self.read_pos.load(Ordering::Acquire);
if self.data.len() < old_pos + len {
return None;
}
Some(&self.data[old_pos..old_pos + len])
}
}

struct FuzzEstimator {
Expand Down Expand Up @@ -92,6 +117,8 @@ impl SocketDescriptor for Peer {

#[inline]
pub fn do_test(data: &[u8]) {
reset_rng_state();

let input = Arc::new(InputData {
data: data.to_vec(),
read_pos: AtomicUsize::new(0),
Expand Down Expand Up @@ -137,6 +164,12 @@ pub fn do_test(data: &[u8]) {
}, our_network_key);

let mut peers = [false; 256];
let mut should_forward = false;
let mut payments_received = Vec::new();
let mut payments_sent = 0;
let mut pending_funding_generation: Vec<(Uint256, u64, Script)> = Vec::new();
let mut pending_funding_signatures = HashMap::new();
let mut pending_funding_relay = Vec::new();

loop {
match get_slice!(1)[0] {
Expand Down Expand Up @@ -178,8 +211,122 @@ pub fn do_test(data: &[u8]) {
Err(_) => { peers[peer_id as usize] = false; }
}
},
4 => {
let value = slice_to_be24(get_slice!(3)) as u64;
let route = match router.get_route(&get_pubkey!(), &Vec::new(), value, 42) {
Ok(route) => route,
Err(_) => return,
};
let mut payment_hash = [0; 32];
payment_hash[0..8].copy_from_slice(&be64_to_array(payments_sent));
let mut sha = Sha256::new();
sha.input(&payment_hash);
sha.result(&mut payment_hash);
for i in 1..32 { payment_hash[i] = 0; }
payments_sent += 1;
match channelmanager.send_payment(route, payment_hash) {
Ok(_) => {},
Err(_) => return,
}
},
5 => {
let peer_id = get_slice!(1)[0];
if !peers[peer_id as usize] { return; }
let their_key = get_pubkey!();
let chan_value = slice_to_be24(get_slice!(3)) as u64;
if channelmanager.create_channel(their_key, chan_value, 0).is_err() { return; }
},
6 => {
let mut channels = channelmanager.list_channels();
let channel_id = get_slice!(1)[0] as usize;
if channel_id >= channels.len() { return; }
channels.sort_by(|a, b| { a.channel_id.cmp(&b.channel_id) });
if channelmanager.close_channel(&channels[channel_id].channel_id).is_err() { return; }
},
7 => {
if should_forward {
channelmanager.process_pending_htlc_forward();
handler.process_events();
should_forward = false;
}
},
8 => {
for payment in payments_received.drain(..) {
let mut payment_preimage = None;
for i in 0..payments_sent {
let mut payment_hash = [0; 32];
payment_hash[0..8].copy_from_slice(&be64_to_array(i));
let mut sha = Sha256::new();
sha.input(&payment_hash);
sha.result(&mut payment_hash);
for i in 1..32 { payment_hash[i] = 0; }
if payment_hash == payment {
payment_hash = [0; 32];
payment_hash[0..8].copy_from_slice(&be64_to_array(i));
payment_preimage = Some(payment_hash);
break;
}
}
channelmanager.claim_funds(payment_preimage.unwrap());
}
},
9 => {
for payment in payments_received.drain(..) {
channelmanager.fail_htlc_backwards(&payment);
}
},
10 => {
for funding_generation in pending_funding_generation.drain(..) {
let mut tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: vec![TxOut {
value: funding_generation.1, script_pubkey: funding_generation.2,
}] };
let funding_output = (Sha256dHash::from_data(&serialize(&tx).unwrap()[..]), 0);
channelmanager.funding_transaction_generated(&funding_generation.0, funding_output.clone());
pending_funding_signatures.insert(funding_output, tx);
}
},
11 => {
if !pending_funding_relay.is_empty() {
let mut txn = Vec::with_capacity(pending_funding_relay.len());
let mut txn_idxs = Vec::with_capacity(pending_funding_relay.len());
for (idx, tx) in pending_funding_relay.iter().enumerate() {
txn.push(tx);
txn_idxs.push(idx as u32 + 1);
}

let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
channelmanager.block_connected(&header, 1, &txn[..], &txn_idxs[..]);
txn.clear();
txn_idxs.clear();
for i in 2..100 {
header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
channelmanager.block_connected(&header, i, &txn[..], &txn_idxs[..]);
}
}
pending_funding_relay.clear();
},
_ => return,
}
for event in handler.get_and_clear_pending_events() {
match event {
Event::FundingGenerationReady { temporary_channel_id, channel_value_satoshis, output_script, .. } => {
pending_funding_generation.push((temporary_channel_id, channel_value_satoshis, output_script));
},
Event::FundingBroadcastSafe { funding_txo, .. } => {
pending_funding_relay.push(pending_funding_signatures.remove(&funding_txo).unwrap());
},
Event::PaymentReceived { payment_hash, .. } => {
payments_received.push(payment_hash);
},
Event::PaymentSent {..} => {},
Event::PaymentFailed {..} => {},

Event::PendingHTLCsForwardable {..} => {
should_forward = true;
},
_ => panic!("Unknown event"),
}
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions fuzz/fuzz_targets/peer_crypt_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ extern crate lightning;
extern crate secp256k1;

use lightning::ln::peer_channel_encryptor::PeerChannelEncryptor;
use lightning::util::reset_rng_state;

use secp256k1::key::{PublicKey,SecretKey};
use secp256k1::Secp256k1;
Expand All @@ -14,6 +15,8 @@ fn slice_to_be16(v: &[u8]) -> u16 {

#[inline]
pub fn do_test(data: &[u8]) {
reset_rng_state();

let mut read_pos = 0;
macro_rules! get_slice {
($len: expr) => {
Expand Down
14 changes: 10 additions & 4 deletions src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1639,10 +1639,16 @@ impl Channel {
if non_shutdown_state & !(ChannelState::TheirFundingLocked as u32) == ChannelState::FundingSent as u32 {
for (ref tx, index_in_block) in txn_matched.iter().zip(indexes_of_txn_matched) {
if tx.txid() == self.channel_monitor.get_funding_txo().unwrap().0 {
self.funding_tx_confirmations = 1;
self.short_channel_id = Some(((height as u64) << (5*8)) |
((*index_in_block as u64) << (2*8)) |
((self.channel_monitor.get_funding_txo().unwrap().1 as u64) << (2*8)));
let txo_idx = self.channel_monitor.get_funding_txo().unwrap().1 as usize;
if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.get_funding_redeemscript().to_v0_p2wsh() ||
tx.output[txo_idx].value != self.channel_value_satoshis {
self.channel_state = ChannelState::ShutdownComplete as u32;
} else {
self.funding_tx_confirmations = 1;
self.short_channel_id = Some(((height as u64) << (5*8)) |
((*index_in_block as u64) << (2*8)) |
((self.channel_monitor.get_funding_txo().unwrap().1 as u64) << (2*8)));
}
}
}
}
Expand Down
Loading