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.
* 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.
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 }
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};
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;
#[inline]
pub fn do_test(data: &[u8]) {
+ reset_rng_state();
+
let input = InputData {
data,
read_pos: AtomicUsize::new(0),
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)
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));
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;
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};
((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) |
((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,
}
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 {
#[inline]
pub fn do_test(data: &[u8]) {
+ reset_rng_state();
+
let input = Arc::new(InputData {
data: data.to_vec(),
read_pos: AtomicUsize::new(0),
}, 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] {
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"),
+ }
+ }
}
}
extern crate secp256k1;
use lightning::ln::peer_channel_encryptor::PeerChannelEncryptor;
+use lightning::util::reset_rng_state;
use secp256k1::key::{PublicKey,SecretKey};
use secp256k1::Secp256k1;
#[inline]
pub fn do_test(data: &[u8]) {
+ reset_rng_state();
+
let mut read_pos = 0;
macro_rules! get_slice {
($len: expr) => {
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)));
+ }
}
}
}
},
None => {}
}
+ //TODO: Check if channel was closed (or disabled) here
}
for to_insert in short_to_ids_to_insert {
channel_state.short_to_id.insert(to_insert.0, to_insert.1);
use bitcoin::util::hash::Sha256dHash;
use bitcoin::util::uint::Uint256;
use bitcoin::blockdata::block::BlockHeader;
- use bitcoin::blockdata::transaction::Transaction;
+ use bitcoin::blockdata::transaction::{Transaction, TxOut};
use bitcoin::network::constants::Network;
use bitcoin::network::serialize::serialize;
use bitcoin::network::serialize::BitcoinHash;
node_a.handle_accept_channel(&node_b.get_our_node_id(), &accept_chan).unwrap();
let chan_id = unsafe { CHAN_COUNT };
- let tx = Transaction { version: chan_id as u32, lock_time: 0, input: Vec::new(), output: Vec::new() };
- let funding_output = (Sha256dHash::from_data(&serialize(&tx).unwrap()[..]), chan_id);
+ let tx;
+ let funding_output;
let events_1 = node_a.get_and_clear_pending_events();
assert_eq!(events_1.len(), 1);
match events_1[0] {
- Event::FundingGenerationReady { ref temporary_channel_id, ref channel_value_satoshis, output_script: _, user_channel_id } => {
+ Event::FundingGenerationReady { ref temporary_channel_id, ref channel_value_satoshis, ref output_script, user_channel_id } => {
assert_eq!(*channel_value_satoshis, 100000);
assert_eq!(user_channel_id, 42);
+ tx = Transaction { version: chan_id as u32, lock_time: 0, input: Vec::new(), output: vec![TxOut {
+ value: *channel_value_satoshis, script_pubkey: output_script.clone(),
+ }]};
+ funding_output = (Sha256dHash::from_data(&serialize(&tx).unwrap()[..]), 0);
+
node_a.funding_transaction_generated(&temporary_channel_id, funding_output.clone());
//TODO: Check that we got added to chan_monitor_a!
},
use secp256k1::{Secp256k1, Signature};
use bitcoin::util::uint::Uint256;
use bitcoin::util::hash::Sha256dHash;
-use bitcoin::network::serialize::deserialize;
+use bitcoin::network::serialize::{deserialize,serialize};
use bitcoin::blockdata::script::Script;
use std::error::Error;
let len = byte_utils::slice_to_be16(&v[0..2]) as usize;
if v.len() < len + 2 { return Err(DecodeError::WrongLength); }
let mut flags = Vec::with_capacity(len);
- flags.extend_from_slice(&v[2..]);
+ flags.extend_from_slice(&v[2..2 + len]);
Ok(Self {
flags: flags
})
let len = byte_utils::slice_to_be16(&v[0..2]) as usize;
if v.len() < len + 2 { return Err(DecodeError::WrongLength); }
let mut flags = Vec::with_capacity(len);
- flags.extend_from_slice(&v[2..]);
+ flags.extend_from_slice(&v[2..2 + len]);
Ok(Self {
flags: flags
})
}
impl MsgEncodable for AcceptChannel {
fn encode(&self) -> Vec<u8> {
- unimplemented!();
+ let mut res = match &self.shutdown_scriptpubkey {
+ &Some(ref script) => Vec::with_capacity(270 + 2 + script.len()),
+ &None => Vec::with_capacity(270),
+ };
+ res.extend_from_slice(&serialize(&self.temporary_channel_id).unwrap()[..]);
+ res.extend_from_slice(&byte_utils::be64_to_array(self.dust_limit_satoshis));
+ res.extend_from_slice(&byte_utils::be64_to_array(self.max_htlc_value_in_flight_msat));
+ res.extend_from_slice(&byte_utils::be64_to_array(self.channel_reserve_satoshis));
+ res.extend_from_slice(&byte_utils::be64_to_array(self.htlc_minimum_msat));
+ res.extend_from_slice(&byte_utils::be32_to_array(self.minimum_depth));
+ res.extend_from_slice(&byte_utils::be16_to_array(self.to_self_delay));
+ res.extend_from_slice(&byte_utils::be16_to_array(self.max_accepted_htlcs));
+ res.extend_from_slice(&self.funding_pubkey.serialize());
+ res.extend_from_slice(&self.revocation_basepoint.serialize());
+ res.extend_from_slice(&self.payment_basepoint.serialize());
+ res.extend_from_slice(&self.delayed_payment_basepoint.serialize());
+ res.extend_from_slice(&self.htlc_basepoint.serialize());
+ res.extend_from_slice(&self.first_per_commitment_point.serialize());
+ if let &Some(ref script) = &self.shutdown_scriptpubkey {
+ res.extend_from_slice(&byte_utils::be16_to_array(script.len() as u16));
+ res.extend_from_slice(&script[..]);
+ }
+ res
}
}
}
impl MsgEncodable for FundingCreated {
fn encode(&self) -> Vec<u8> {
- unimplemented!();
+ let mut res = Vec::with_capacity(32+32+2+64);
+ res.extend_from_slice(&serialize(&self.temporary_channel_id).unwrap()[..]);
+ res.extend_from_slice(&serialize(&self.funding_txid).unwrap()[..]);
+ res.extend_from_slice(&byte_utils::be16_to_array(self.funding_output_index));
+ let secp_ctx = Secp256k1::without_caps();
+ res.extend_from_slice(&self.signature.serialize_compact(&secp_ctx));
+ res
}
}
}
impl MsgEncodable for FundingLocked {
fn encode(&self) -> Vec<u8> {
- unimplemented!();
+ let mut res = Vec::with_capacity(32+33);
+ res.extend_from_slice(&serialize(&self.channel_id).unwrap());
+ res.extend_from_slice(&self.next_per_commitment_point.serialize());
+ res
}
}
}
impl MsgEncodable for AnnouncementSignatures {
fn encode(&self) -> Vec<u8> {
- unimplemented!();
+ let mut res = Vec::with_capacity(32+8+64*2);
+ res.extend_from_slice(&serialize(&self.channel_id).unwrap());
+ res.extend_from_slice(&byte_utils::be64_to_array(self.short_channel_id));
+ let secp_ctx = Secp256k1::without_caps();
+ res.extend_from_slice(&self.node_signature.serialize_compact(&secp_ctx));
+ res.extend_from_slice(&self.bitcoin_signature.serialize_compact(&secp_ctx));
+ res
}
}
let act_three = try_potential_handleerror!(peer.channel_encryptor.process_act_two(&peer.pending_read_buffer[..], &self.our_node_secret)).to_vec();
peer.pending_outbound_buffer.push_back(act_three);
peer.pending_read_buffer = [0; 18].to_vec(); // Message length header is 18 bytes
+ peer.pending_read_is_header = true;
insert_node_id = Some(peer.their_node_id.unwrap());
encode_and_send_msg!(msgs::Init {
if msg.local_features.requires_unknown_bits() {
return Err(PeerHandleError{});
}
- //TODO: Store features!
+ //TODO: Store features (and check that we've
+ //received Init prior to any other messages)!
+ //TODO: Respond to Init with Init if we're inbound.
},
17 => {
// Error msg
pub(crate) mod rng;
pub(crate) mod sha2;
+#[cfg(feature = "fuzztarget")]
+pub use self::rng::reset_rng_state;
+
#[cfg(test)]
pub(crate) mod test_utils;
#[cfg(feature = "fuzztarget")]
mod fuzzy_rng {
use bitcoin::util::uint::Uint256;
+ use util::byte_utils;
+
+ static mut RNG_ITER: u64 = 0;
pub fn fill_bytes(data: &mut [u8]) {
- for i in 0..data.len() {
- data[i] = 0x42;
+ let rng = unsafe { RNG_ITER += 1; RNG_ITER -1 };
+ for i in 0..data.len() / 8 {
+ data[i*8..(i+1)*8].copy_from_slice(&byte_utils::be64_to_array(rng));
}
+ let rem = data.len() % 8;
+ let off = data.len() - rem;
+ data[off..].copy_from_slice(&byte_utils::be64_to_array(rng)[0..rem]);
}
pub fn rand_uint256() -> Uint256 {
- Uint256([0xdeadbeef, 0x1badcafe, 0xbadbeef, 0xdeadcafe])
+ let rng = unsafe { RNG_ITER += 1; RNG_ITER - 1 };
+ Uint256([rng, rng, rng, rng])
}
pub fn rand_f32() -> f32 {
- 0.42
+ let rng = unsafe { RNG_ITER += 1; RNG_ITER - 1 };
+ f64::from_bits(rng) as f32
+ }
+
+ pub fn reset_rng_state() {
+ unsafe { RNG_ITER = 0; }
}
}
#[cfg(feature = "fuzztarget")]