publish = false
# Because the function is unused it gets dropped before we link lightning, so
# we have to duplicate build.rs here. Note that this is only required for
-# fuzztarget mode.
+# fuzzing mode.
[package.metadata]
cargo-fuzz = true
[dependencies]
afl = { version = "0.4", optional = true }
-lightning = { path = "../lightning", features = ["fuzztarget"] }
+lightning = { path = "../lightning", features = ["regex"] }
bitcoin = { version = "0.27", features = ["fuzztarget", "secp-lowmemory"] }
hex = "0.3"
honggfuzz = { version = "0.5", optional = true }
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::chanmon_consistency::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::chanmon_deser::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::full_stack::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_accept_channel::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_announcement_signatures::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_channel_announcement::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_channel_reestablish::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_channel_update::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_closing_signed::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_commitment_signed::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_decoded_onion_error_packet::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_error_message::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_funding_created::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_funding_locked::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_funding_signed::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_gossip_timestamp_filter::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_init::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_node_announcement::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_onion_hop_data::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_open_channel::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_ping::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_pong::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_query_channel_range::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_query_short_channel_ids::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_reply_channel_range::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_reply_short_channel_ids_end::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_revoke_and_ack::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_shutdown::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_update_add_htlc::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_update_fail_htlc::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_update_fail_malformed_htlc::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_update_fee::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::msg_targets::msg_update_fulfill_htlc::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::peer_crypt::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::router::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::TARGET_MOD::*;
#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
extern crate lightning_fuzz;
use lightning_fuzz::zbase32::*;
chunked_transfer = { version = "1.4", optional = true }
[dev-dependencies]
-tokio = { version = "1.0", features = [ "macros", "rt" ] }
+tokio = { version = "~1.14", features = [ "macros", "rt" ] }
[dependencies]
honggfuzz = { version = "0.5", optional = true }
afl = { version = "0.4", optional = true }
-lightning-invoice = { path = ".."}
+lightning-invoice = { path = ".." }
+lightning = { path = "../../lightning", features = ["regex"] }
bech32 = "0.8"
# Prevent this from interfering with workspaces
tokio = { version = "1.0", features = [ "io-util", "macros", "rt", "sync", "net", "time" ] }
[dev-dependencies]
-tokio = { version = "1.0", features = [ "io-util", "macros", "rt", "rt-multi-thread", "sync", "net", "time" ] }
+tokio = { version = "~1.14", features = [ "io-util", "macros", "rt", "rt-multi-thread", "sync", "net", "time" ] }
"Invalid ChannelMonitor file name",
));
}
+ if filename.unwrap().ends_with(".tmp") {
+ // If we were in the middle of committing an new update and crashed, it should be
+ // safe to ignore the update - we should never have returned to the caller and
+ // irrevocably committed to the new state in any way.
+ continue;
+ }
let txid = Txid::from_hex(filename.unwrap().split_at(64).0);
if txid.is_err() {
"""
[features]
-fuzztarget = ["bitcoin/fuzztarget", "regex"]
# Internal test utilities exposed to other repo crates
_test_utils = ["hex", "regex", "bitcoin/bitcoinconsensus"]
# Unlog messages superior at targeted level.
/// This wrapper avoids having to update some of our tests for now as they assume the direct
/// chain::Watch API wherein we mark a monitor fully-updated by just calling
/// channel_monitor_updated once with the highest ID.
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
pub fn force_channel_monitor_updated(&self, funding_txo: OutPoint, monitor_update_id: u64) {
self.pending_monitor_events.lock().unwrap().push(MonitorEvent::UpdateCompleted {
funding_txo,
});
}
- #[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
+ #[cfg(any(test, fuzzing, feature = "_test_utils"))]
pub fn get_and_clear_pending_events(&self) -> Vec<events::Event> {
use util::events::EventsProvider;
let events = core::cell::RefCell::new(Vec::new());
// We should never ever trigger this from within ChannelManager. Technically a
// user could use this object with some proxying in between which makes this
// possible, but in tests and fuzzing, this should be a panic.
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
panic!("ChannelManager generated a channel update for a channel that was not yet registered!");
- #[cfg(not(any(test, feature = "fuzztarget")))]
+ #[cfg(not(any(test, fuzzing)))]
Err(ChannelMonitorUpdateErr::PermanentFailure)
},
Some(monitor_state) => {
/// An update generated by the underlying Channel itself which contains some new information the
/// ChannelMonitor should be made aware of.
-#[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))]
+#[cfg_attr(any(test, fuzzing, feature = "_test_utils"), derive(PartialEq))]
#[derive(Clone)]
#[must_use]
pub struct ChannelMonitorUpdate {
);
-#[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))]
+#[cfg_attr(any(test, fuzzing, feature = "_test_utils"), derive(PartialEq))]
#[derive(Clone)]
pub(crate) enum ChannelMonitorUpdateStep {
LatestHolderCommitmentTXInfo {
/// Transaction outputs to watch for on-chain spends.
pub type TransactionOutputs = (Txid, Vec<(u32, TxOut)>);
-#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
-/// Used only in testing and fuzztarget to check serialization roundtrips don't change the
-/// underlying object
+#[cfg(any(test, fuzzing, feature = "_test_utils"))]
+/// Used only in testing and fuzzing to check serialization roundtrips don't change the underlying
+/// object
impl<Signer: Sign> PartialEq for ChannelMonitor<Signer> {
fn eq(&self, other: &Self) -> bool {
let inner = self.inner.lock().unwrap();
}
}
-#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
-/// Used only in testing and fuzztarget to check serialization roundtrips don't change the
-/// underlying object
+#[cfg(any(test, fuzzing, feature = "_test_utils"))]
+/// Used only in testing and fuzzing to check serialization roundtrips don't change the underlying
+/// object
impl<Signer: Sign> PartialEq for ChannelMonitorImpl<Signer> {
fn eq(&self, other: &Self) -> bool {
if self.latest_update_id != other.latest_update_id ||
//! generated/etc. This makes it a good candidate for tight integration into an existing wallet
//! instead of having a rather-separate lightning appendage to a wallet.
-#![cfg_attr(not(any(test, feature = "fuzztarget", feature = "_test_utils")), deny(missing_docs))]
-#![cfg_attr(not(any(test, feature = "fuzztarget", feature = "_test_utils")), forbid(unsafe_code))]
+#![cfg_attr(not(any(test, fuzzing, feature = "_test_utils")), deny(missing_docs))]
+#![cfg_attr(not(any(test, fuzzing, feature = "_test_utils")), forbid(unsafe_code))]
#![deny(broken_intra_doc_links)]
// In general, rust is absolutely horrid at supporting users doing things like,
#[cfg(not(any(feature = "std", feature = "no-std")))]
compile_error!("at least one of the `std` or `no-std` features must be enabled");
+#[cfg(all(fuzzing, test))]
+compile_error!("Tests will always fail with cfg=fuzzing");
+
#[macro_use]
extern crate alloc;
extern crate bitcoin;
extern crate core;
#[cfg(any(test, feature = "_test_utils"))] extern crate hex;
-#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))] extern crate regex;
+#[cfg(any(test, fuzzing, feature = "_test_utils"))] extern crate regex;
#[cfg(not(feature = "std"))] extern crate core2;
if let &Some(ref b_htlcout) = b {
a_htlcout.cltv_expiry.cmp(&b_htlcout.cltv_expiry)
// Note that due to hash collisions, we have to have a fallback comparison
- // here for fuzztarget mode (otherwise at least chanmon_fail_consistency
+ // here for fuzzing mode (otherwise at least chanmon_fail_consistency
// may fail)!
.then(a_htlcout.payment_hash.0.cmp(&b_htlcout.payment_hash.0))
// For non-HTLC outputs, if they're copying our SPK we don't really care if we
use prelude::*;
use core::{cmp,mem,fmt};
use core::ops::Deref;
-#[cfg(any(test, feature = "fuzztarget", debug_assertions))]
+#[cfg(any(test, fuzzing, debug_assertions))]
use sync::Mutex;
use bitcoin::hashes::hex::ToHex;
// `next_remote_commit_tx_fee_msat` properly predict what the next commitment transaction fee will
// be, by comparing the cached values to the fee of the tranaction generated by
// `build_commitment_transaction`.
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_local_commitment_tx_fee_info_cached: Mutex<Option<CommitmentTxInfoCached>>,
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_remote_commitment_tx_fee_info_cached: Mutex<Option<CommitmentTxInfoCached>>,
/// lnd has a long-standing bug where, upon reconnection, if the channel is not yet confirmed
/// See-also <https://github.com/lightningnetwork/lnd/issues/4006>
pub workaround_lnd_bug_4006: Option<msgs::FundingLocked>,
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
// When we receive an HTLC fulfill on an outbound path, we may immediately fulfill the
// corresponding HTLC on the inbound path. If, then, the outbound path channel is
// disconnected and reconnected (before we've exchange commitment_signed and revoke_and_ack
channel_type: ChannelTypeFeatures,
}
-#[cfg(any(test, feature = "fuzztarget"))]
+#[cfg(any(test, fuzzing))]
struct CommitmentTxInfoCached {
fee: u64,
total_pending_htlcs: usize,
announcement_sigs: None,
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
workaround_lnd_bug_4006: None,
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
historical_inbound_htlc_fulfills: HashSet::new(),
// We currently only actually support one channel type, so don't retry with new types
announcement_sigs: None,
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
workaround_lnd_bug_4006: None,
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
historical_inbound_htlc_fulfills: HashSet::new(),
channel_type,
}
}
if pending_idx == core::usize::MAX {
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
// If we failed to find an HTLC to fulfill, make sure it was previously fulfilled and
// this is simply a duplicate claim, not previously failed and we lost funds.
debug_assert!(self.historical_inbound_htlc_fulfills.contains(&htlc_id_arg));
if htlc_id_arg == htlc_id {
// Make sure we don't leave latest_monitor_update_id incremented here:
self.latest_monitor_update_id -= 1;
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
debug_assert!(self.historical_inbound_htlc_fulfills.contains(&htlc_id_arg));
return UpdateFulfillFetch::DuplicateClaim {};
}
self.holding_cell_htlc_updates.push(HTLCUpdateAwaitingACK::ClaimHTLC {
payment_preimage: payment_preimage_arg, htlc_id: htlc_id_arg,
});
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
self.historical_inbound_htlc_fulfills.insert(htlc_id_arg);
return UpdateFulfillFetch::NewClaim { monitor_update, htlc_value_msat, msg: None };
}
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
self.historical_inbound_htlc_fulfills.insert(htlc_id_arg);
{
}
}
if pending_idx == core::usize::MAX {
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
// If we failed to find an HTLC to fail, make sure it was previously fulfilled and this
// is simply a duplicate fail, not previously failed and we failed-back too early.
debug_assert!(self.historical_inbound_htlc_fulfills.contains(&htlc_id_arg));
match pending_update {
&HTLCUpdateAwaitingACK::ClaimHTLC { htlc_id, .. } => {
if htlc_id_arg == htlc_id {
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
debug_assert!(self.historical_inbound_htlc_fulfills.contains(&htlc_id_arg));
return Ok(None);
}
return Err(ChannelError::Close("Minimum confirmation depth must be at least 1".to_owned()));
}
+ if let Some(ty) = &msg.channel_type {
+ if *ty != self.channel_type {
+ return Err(ChannelError::Close("Channel Type in accept_channel didn't match the one sent in open_channel.".to_owned()));
+ }
+ } else if their_features.supports_channel_type() {
+ // Assume they've accepted the channel type as they said they understand it.
+ } else {
+ self.channel_type = ChannelTypeFeatures::from_counterparty_init(&their_features)
+ }
+
let counterparty_shutdown_scriptpubkey = if their_features.supports_upfront_shutdown_script() {
match &msg.shutdown_scriptpubkey {
&OptionalField::Present(ref script) => {
/// Returns transaction if there is pending funding transaction that is yet to broadcast
pub fn unbroadcasted_funding(&self) -> Option<Transaction> {
- if self.channel_state & (ChannelState::FundingCreated as u32) != 0 {
- self.funding_transaction.clone()
- } else {
- None
- }
+ if self.channel_state & (ChannelState::FundingCreated as u32) != 0 {
+ self.funding_transaction.clone()
+ } else {
+ None
+ }
}
/// Returns a HTLCStats about inbound pending htlcs
let num_htlcs = included_htlcs + addl_htlcs;
let res = Self::commit_tx_fee_msat(self.feerate_per_kw, num_htlcs, self.opt_anchors());
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
{
let mut fee = res;
if fee_spike_buffer_htlc.is_some() {
let num_htlcs = included_htlcs + addl_htlcs;
let res = Self::commit_tx_fee_msat(self.feerate_per_kw, num_htlcs, self.opt_anchors());
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
{
let mut fee = res;
if fee_spike_buffer_htlc.is_some() {
return Err((None, ChannelError::Close("Funding remote cannot afford proposed new fee".to_owned())));
}
}
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
{
if self.is_outbound() {
let projected_commit_tx_info = self.next_local_commitment_tx_fee_info_cached.lock().unwrap().take();
return Err(ChannelError::Close("Received an unexpected revoke_and_ack".to_owned()));
}
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
{
*self.next_local_commitment_tx_fee_info_cached.lock().unwrap() = None;
*self.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None;
// If we generated the funding transaction and it doesn't match what it
// should, the client is really broken and we should just panic and
// tell them off. That said, because hash collisions happen with high
- // probability in fuzztarget mode, if we're fuzzing we just close the
+ // probability in fuzzing mode, if we're fuzzing we just close the
// channel and move on.
- #[cfg(not(feature = "fuzztarget"))]
+ #[cfg(not(fuzzing))]
panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
}
self.update_time_counter += 1;
if input.witness.is_empty() {
// We generated a malleable funding transaction, implying we've
// just exposed ourselves to funds loss to our counterparty.
- #[cfg(not(feature = "fuzztarget"))]
+ #[cfg(not(fuzzing))]
panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
}
}
Some(script) => script.clone().into_inner(),
None => Builder::new().into_script(),
}),
+ channel_type: Some(self.channel_type.clone()),
}
}
// Prior to static_remotekey, my_current_per_commitment_point was critical to claiming
// current to_remote balances. However, it no longer has any use, and thus is now simply
// set to a dummy (but valid, as required by the spec) public key.
- // fuzztarget mode marks a subset of pubkeys as invalid so that we can hit "invalid pubkey"
+ // fuzzing mode marks a subset of pubkeys as invalid so that we can hit "invalid pubkey"
// branches, but we unwrap it below, so we arbitrarily select a dummy pubkey which is both
- // valid, and valid in fuzztarget mode's arbitrary validity criteria:
+ // valid, and valid in fuzzing mode's arbitrary validity criteria:
let mut pk = [2; 33]; pk[1] = 0xff;
let dummy_pubkey = PublicKey::from_slice(&pk).unwrap();
let data_loss_protect = if self.cur_counterparty_commitment_transaction_number + 1 < INITIAL_COMMITMENT_NUMBER {
let counterparty_commitment_txid = commitment_stats.tx.trust().txid();
let (signature, htlc_signatures);
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
{
if !self.is_outbound() {
let projected_commit_tx_info = self.next_remote_commitment_tx_fee_info_cached.lock().unwrap().take();
self.channel_update_status.write(writer)?;
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
(self.historical_inbound_htlc_fulfills.len() as u64).write(writer)?;
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
for htlc in self.historical_inbound_htlc_fulfills.iter() {
htlc.write(writer)?;
}
let channel_update_status = Readable::read(reader)?;
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
let mut historical_inbound_htlc_fulfills = HashSet::new();
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
{
let htlc_fulfills_len: u64 = Readable::read(reader)?;
for _ in 0..htlc_fulfills_len {
announcement_sigs,
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
workaround_lnd_bug_4006: None,
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
historical_inbound_htlc_fulfills,
channel_type: channel_type.unwrap(),
let mut channel_state = self.channel_state.lock().unwrap();
match channel_state.by_id.entry(temporary_channel_id) {
hash_map::Entry::Occupied(_) => {
- if cfg!(feature = "fuzztarget") {
+ if cfg!(fuzzing) {
return Err(APIError::APIMisuseError { err: "Fuzzy bad RNG".to_owned() });
} else {
panic!("RNG is bad???");
macro_rules! check_total_value {
($payment_data_total_msat: expr, $payment_secret: expr, $payment_preimage: expr) => {{
- let mut total_value = 0;
let mut payment_received_generated = false;
let htlcs = channel_state.claimable_htlcs.entry(payment_hash)
.or_insert(Vec::new());
continue
}
}
- htlcs.push(claimable_htlc);
+ let mut total_value = claimable_htlc.value;
for htlc in htlcs.iter() {
total_value += htlc.value;
match &htlc.onion_payload {
if total_value >= msgs::MAX_VALUE_MSAT || total_value > $payment_data_total_msat {
log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the total value {} ran over expected value {} (or HTLCs were inconsistent)",
log_bytes!(payment_hash.0), total_value, $payment_data_total_msat);
- for htlc in htlcs.iter() {
- fail_htlc!(htlc);
- }
+ fail_htlc!(claimable_htlc);
} else if total_value == $payment_data_total_msat {
+ htlcs.push(claimable_htlc);
new_events.push(events::Event::PaymentReceived {
payment_hash,
purpose: events::PaymentPurpose::InvoicePayment {
// Nothing to do - we haven't reached the total
// payment value yet, wait until we receive more
// MPP parts.
+ htlcs.push(claimable_htlc);
}
payment_received_generated
}}
/// In chanmon_consistency_target, we'd like to be able to restore monitor updating without
/// handling all pending events (i.e. not PendingHTLCsForwardable). Thus, we expose monitor
/// update events as a separate process method here.
- #[cfg(feature = "fuzztarget")]
+ #[cfg(fuzzing)]
pub fn process_monitor_events(&self) {
self.process_pending_monitor_events();
}
}
}
- #[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
+ #[cfg(any(test, fuzzing, feature = "_test_utils"))]
pub fn get_and_clear_pending_events(&self) -> Vec<events::Event> {
let events = core::cell::RefCell::new(Vec::new());
let event_handler = |event: &events::Event| events.borrow_mut().push(event.clone());
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage_2);
}
+#[test]
+fn test_dup_htlc_second_fail_panic() {
+ // Previously, if we received two HTLCs back-to-back, where the second overran the expected
+ // value for the payment, we'd fail back both HTLCs after generating a `PaymentReceived` event.
+ // Then, if the user failed the second payment, they'd hit a "tried to fail an already failed
+ // HTLC" debug panic. This tests for this behavior, checking that only one HTLC is auto-failed.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001, InitFeatures::known(), InitFeatures::known());
+
+ let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id())
+ .with_features(InvoiceFeatures::known());
+ let scorer = test_utils::TestScorer::with_penalty(0);
+ let route = get_route(
+ &nodes[0].node.get_our_node_id(), &payment_params, &nodes[0].network_graph,
+ Some(&nodes[0].node.list_usable_channels().iter().collect::<Vec<_>>()),
+ 10_000, TEST_FINAL_CLTV, nodes[0].logger, &scorer).unwrap();
+
+ let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(&nodes[1]);
+
+ {
+ nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
+ check_added_monitors!(nodes[0], 1);
+ let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ let mut payment_event = SendEvent::from_event(events.pop().unwrap());
+ nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
+ commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false);
+ }
+ expect_pending_htlcs_forwardable!(nodes[1]);
+ expect_payment_received!(nodes[1], our_payment_hash, our_payment_secret, 10_000);
+
+ {
+ nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
+ check_added_monitors!(nodes[0], 1);
+ let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ let mut payment_event = SendEvent::from_event(events.pop().unwrap());
+ nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
+ commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false);
+ // At this point, nodes[1] would notice it has too much value for the payment. It will
+ // assume the second is a privacy attack (no longer particularly relevant
+ // post-payment_secrets) and fail back the new HTLC. Previously, it'd also have failed back
+ // the first HTLC delivered above.
+ }
+
+ // Now we go fail back the first HTLC from the user end.
+ expect_pending_htlcs_forwardable_ignore!(nodes[1]);
+ nodes[1].node.process_pending_htlc_forwards();
+ nodes[1].node.fail_htlc_backwards(&our_payment_hash);
+
+ expect_pending_htlcs_forwardable_ignore!(nodes[1]);
+ nodes[1].node.process_pending_htlc_forwards();
+
+ check_added_monitors!(nodes[1], 1);
+ let fail_updates_1 = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+ assert_eq!(fail_updates_1.update_fail_htlcs.len(), 2);
+
+ nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &fail_updates_1.update_fail_htlcs[0]);
+ nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &fail_updates_1.update_fail_htlcs[1]);
+ commitment_signed_dance!(nodes[0], nodes[1], fail_updates_1.commitment_signed, false);
+
+ let failure_events = nodes[0].node.get_and_clear_pending_events();
+ assert_eq!(failure_events.len(), 2);
+ if let Event::PaymentPathFailed { .. } = failure_events[0] {} else { panic!(); }
+ if let Event::PaymentPathFailed { .. } = failure_events[1] {} else { panic!(); }
+}
+
#[test]
fn test_keysend_payments_to_public_node() {
let chanmon_cfgs = create_chanmon_cfgs(2);
pub mod features;
pub mod script;
-#[cfg(feature = "fuzztarget")]
+#[cfg(fuzzing)]
pub mod peer_channel_encryptor;
-#[cfg(not(feature = "fuzztarget"))]
+#[cfg(not(fuzzing))]
pub(crate) mod peer_channel_encryptor;
-#[cfg(feature = "fuzztarget")]
+#[cfg(fuzzing)]
pub mod channel;
-#[cfg(not(feature = "fuzztarget"))]
+#[cfg(not(fuzzing))]
pub(crate) mod channel;
mod onion_utils;
pub first_per_commitment_point: PublicKey,
/// Optionally, a request to pre-set the to-sender output's scriptPubkey for when we collaboratively close
pub shutdown_scriptpubkey: OptionalField<Script>,
+ /// The channel type that this channel will represent. If none is set, we derive the channel
+ /// type from the intersection of our feature bits with our counterparty's feature bits from
+ /// the Init message.
+ ///
+ /// This is required to match the equivalent field in [`OpenChannel::channel_type`].
+ pub channel_type: Option<ChannelTypeFeatures>,
}
/// A funding_created message to be sent or received from a peer
pub(crate) pad: Vec<u8>,
}
}
-#[cfg(feature = "fuzztarget")]
+#[cfg(fuzzing)]
pub use self::fuzzy_internal_msgs::*;
-#[cfg(not(feature = "fuzztarget"))]
+#[cfg(not(fuzzing))]
pub(crate) use self::fuzzy_internal_msgs::*;
#[derive(Clone)]
htlc_basepoint,
first_per_commitment_point,
shutdown_scriptpubkey
-}, {});
+}, {
+ (1, channel_type, option),
+});
impl_writeable_msg!(AnnouncementSignatures, {
channel_id,
delayed_payment_basepoint: pubkey_4,
htlc_basepoint: pubkey_5,
first_per_commitment_point: pubkey_6,
- shutdown_scriptpubkey: if shutdown { OptionalField::Present(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: pubkey_1}, Network::Testnet).script_pubkey()) } else { OptionalField::Absent }
+ shutdown_scriptpubkey: if shutdown { OptionalField::Present(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: pubkey_1}, Network::Testnet).script_pubkey()) } else { OptionalField::Absent },
+ channel_type: None,
};
let encoded_value = accept_channel.encode();
let mut target_value = hex::decode("020202020202020202020202020202020202020202020202020202020202020212345678901234562334032891223698321446687011447600083a840000034d000c89d4c0bcc0bc031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d076602531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33703462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b0362c0a046dacce86ddd0343c6d3c7c79c2208ba0d9c9cf24a6d046d21d21f90f703f006a18d5653c4edf5391ff23a61f03ff83d237e880ee61187fa9f379a028e0a").unwrap();
/// so that we can choose cheaper paths (as per Dijkstra's algorithm).
/// Fee values should be updated only in the context of the whole path, see update_value_and_recompute_fees.
/// These fee values are useful to choose hops as we traverse the graph "payee-to-payer".
-#[derive(Clone, Debug)]
+#[derive(Clone)]
struct PathBuildingHop<'a> {
// Note that this should be dropped in favor of loading it from CandidateRouteHop, but doing so
// is a larger refactor and will require careful performance analysis.
/// decrease as well. Thus, we have to explicitly track which nodes have been processed and
/// avoid processing them again.
was_processed: bool,
- #[cfg(all(not(feature = "_bench_unstable"), any(test, feature = "fuzztarget")))]
+ #[cfg(all(not(feature = "_bench_unstable"), any(test, fuzzing)))]
// In tests, we apply further sanity checks on cases where we skip nodes we already processed
// to ensure it is specifically in cases where the fee has gone down because of a decrease in
// value_contribution_msat, which requires tracking it here. See comments below where it is
value_contribution_msat: u64,
}
+impl<'a> core::fmt::Debug for PathBuildingHop<'a> {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
+ f.debug_struct("PathBuildingHop")
+ .field("node_id", &self.node_id)
+ .field("short_channel_id", &self.candidate.short_channel_id())
+ .field("total_fee_msat", &self.total_fee_msat)
+ .field("next_hops_fee_msat", &self.next_hops_fee_msat)
+ .field("hop_use_fee_msat", &self.hop_use_fee_msat)
+ .field("total_fee_msat - (next_hops_fee_msat + hop_use_fee_msat)", &(&self.total_fee_msat - (&self.next_hops_fee_msat + &self.hop_use_fee_msat)))
+ .field("path_penalty_msat", &self.path_penalty_msat)
+ .field("path_htlc_minimum_msat", &self.path_htlc_minimum_msat)
+ .field("cltv_expiry_delta", &self.candidate.cltv_expiry_delta())
+ .finish()
+ }
+}
+
// Instantiated with a list of hops with correct data in them collected during path finding,
// an instance of this struct should be further modified only via given methods.
#[derive(Clone)]
node_info.features.supports_basic_mpp()
} else { false }
} else { false };
- log_trace!(logger, "Searching for a route from payer {} to payee {} {} MPP", our_node_pubkey,
- payment_params.payee_pubkey, if allow_mpp { "with" } else { "without" });
+ log_trace!(logger, "Searching for a route from payer {} to payee {} {} MPP and {} first hops {}overriding the network graph", our_node_pubkey,
+ payment_params.payee_pubkey, if allow_mpp { "with" } else { "without" },
+ first_hops.map(|hops| hops.len()).unwrap_or(0), if first_hops.is_some() { "" } else { "not " });
// Step (1).
// Prepare the data we'll use for payee-to-payer search by
path_htlc_minimum_msat,
path_penalty_msat: u64::max_value(),
was_processed: false,
- #[cfg(all(not(feature = "_bench_unstable"), any(test, feature = "fuzztarget")))]
+ #[cfg(all(not(feature = "_bench_unstable"), any(test, fuzzing)))]
value_contribution_msat,
}
});
#[allow(unused_mut)] // We only use the mut in cfg(test)
let mut should_process = !old_entry.was_processed;
- #[cfg(all(not(feature = "_bench_unstable"), any(test, feature = "fuzztarget")))]
+ #[cfg(all(not(feature = "_bench_unstable"), any(test, fuzzing)))]
{
// In test/fuzzing builds, we do extra checks to make sure the skipping
// of already-seen nodes only happens in cases we expect (see below).
old_entry.fee_msat = 0; // This value will be later filled with hop_use_fee_msat of the following channel
old_entry.path_htlc_minimum_msat = path_htlc_minimum_msat;
old_entry.path_penalty_msat = path_penalty_msat;
- #[cfg(all(not(feature = "_bench_unstable"), any(test, feature = "fuzztarget")))]
+ #[cfg(all(not(feature = "_bench_unstable"), any(test, fuzzing)))]
{
old_entry.value_contribution_msat = value_contribution_msat;
}
did_add_update_path_to_src_node = true;
} else if old_entry.was_processed && new_cost < old_cost {
- #[cfg(all(not(feature = "_bench_unstable"), any(test, feature = "fuzztarget")))]
+ #[cfg(all(not(feature = "_bench_unstable"), any(test, fuzzing)))]
{
// If we're skipping processing a node which was previously
// processed even though we found another path to it with a
ordered_hops.last_mut().unwrap().0.fee_msat = value_contribution_msat;
ordered_hops.last_mut().unwrap().0.hop_use_fee_msat = 0;
- log_trace!(logger, "Found a path back to us from the target with {} hops contributing up to {} msat: {:?}",
- ordered_hops.len(), value_contribution_msat, ordered_hops);
+ log_trace!(logger, "Found a path back to us from the target with {} hops contributing up to {} msat: \n {:#?}",
+ ordered_hops.len(), value_contribution_msat, ordered_hops.iter().map(|h| &(h.0)).collect::<Vec<&PathBuildingHop>>());
let mut payment_path = PaymentPath {hops: ordered_hops};
use io;
-#[cfg(not(feature = "fuzztarget"))]
+#[cfg(not(fuzzing))]
mod real_chacha {
use core::cmp;
use core::convert::TryInto;
}
}
}
-#[cfg(not(feature = "fuzztarget"))]
+#[cfg(not(fuzzing))]
pub use self::real_chacha::ChaCha20;
-#[cfg(feature = "fuzztarget")]
+#[cfg(fuzzing)]
mod fuzzy_chacha {
pub struct ChaCha20 {}
pub fn process_in_place(&mut self, _input_output: &mut [u8]) {}
}
}
-#[cfg(feature = "fuzztarget")]
+#[cfg(fuzzing)]
pub use self::fuzzy_chacha::ChaCha20;
pub(crate) struct ChaChaReader<'a, R: io::Read> {
// This is a port of Andrew Moons poly1305-donna
// https://github.com/floodyberry/poly1305-donna
-#[cfg(not(feature = "fuzztarget"))]
+#[cfg(not(fuzzing))]
mod real_chachapoly {
use util::chacha20::ChaCha20;
use util::poly1305::Poly1305;
}
}
}
-#[cfg(not(feature = "fuzztarget"))]
+#[cfg(not(fuzzing))]
pub use self::real_chachapoly::ChaCha20Poly1305RFC;
-#[cfg(feature = "fuzztarget")]
+#[cfg(fuzzing)]
mod fuzzy_chachapoly {
#[derive(Clone, Copy)]
pub struct ChaCha20Poly1305RFC {
}
}
}
-#[cfg(feature = "fuzztarget")]
+#[cfg(fuzzing)]
pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC;
macro_rules! hash_to_message {
($slice: expr) => {
{
- #[cfg(not(feature = "fuzztarget"))]
+ #[cfg(not(fuzzing))]
{
::bitcoin::secp256k1::Message::from_slice($slice).unwrap()
}
- #[cfg(feature = "fuzztarget")]
+ #[cfg(fuzzing)]
{
match ::bitcoin::secp256k1::Message::from_slice($slice) {
Ok(msg) => msg,
pub(crate) mod atomic_counter;
pub(crate) mod byte_utils;
pub(crate) mod chacha20;
-#[cfg(feature = "fuzztarget")]
+#[cfg(fuzzing)]
pub mod zbase32;
-#[cfg(not(feature = "fuzztarget"))]
+#[cfg(not(fuzzing))]
pub(crate) mod zbase32;
-#[cfg(not(feature = "fuzztarget"))]
+#[cfg(not(fuzzing))]
pub(crate) mod poly1305;
pub(crate) mod chacha20poly1305rfc;
pub(crate) mod transaction_utils;
pub mod logger;
pub mod config;
-#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
+#[cfg(any(test, fuzzing, feature = "_test_utils"))]
pub mod test_utils;
/// impls of traits that add exra enforcement on the way they're called. Useful for detecting state
/// machine errors and used in fuzz targets and tests.
-#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
+#[cfg(any(test, fuzzing, feature = "_test_utils"))]
pub mod enforcing_trait_impls;