X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=fuzz%2Fsrc%2Fchanmon_consistency.rs;h=8c4f5adcb64ae64abbbc03b0956c556f3abbf44c;hb=46009a5f83e539fc103ea164a3c765a7f014f09a;hp=acdf3cb3ca2dc3b2f902aa1a323e645b837d2368;hpb=d73e43428028b691453b0d5fe5e8608e4e9b1190;p=rust-lightning diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index acdf3cb3..8c4f5adc 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -34,7 +34,7 @@ use lightning::chain::{BestBlock, ChannelMonitorUpdateErr, chainmonitor, channel use lightning::chain::channelmonitor::{ChannelMonitor, MonitorEvent}; use lightning::chain::transaction::OutPoint; use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; -use lightning::chain::keysinterface::{KeysInterface, InMemorySigner}; +use lightning::chain::keysinterface::{KeyMaterial, KeysInterface, InMemorySigner, Recipient}; use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; use lightning::ln::channelmanager::{ChainParameters, ChannelManager, PaymentSendFailure, ChannelManagerReadArgs}; use lightning::ln::channel::FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE; @@ -50,8 +50,7 @@ use lightning::util::events::MessageSendEventsProvider; use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer}; use lightning::routing::router::{Route, RouteHop}; - -use utils::test_logger; +use utils::test_logger::{self, Output}; use utils::test_persister::TestPersister; use bitcoin::secp256k1::key::{PublicKey,SecretKey}; @@ -64,6 +63,7 @@ use std::collections::{HashSet, hash_map, HashMap}; use std::sync::{Arc,Mutex}; use std::sync::atomic; use std::io::Cursor; +use bitcoin::bech32::u5; const MAX_FEE: u32 = 10_000; struct FuzzEstimator { @@ -161,8 +161,12 @@ struct KeyProvider { impl KeysInterface for KeyProvider { type Signer = EnforcingSigner; - fn get_node_secret(&self) -> SecretKey { - SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, self.node_id]).unwrap() + fn get_node_secret(&self, _recipient: Recipient) -> Result { + Ok(SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, self.node_id]).unwrap()) + } + + fn get_inbound_payment_key_material(&self) -> KeyMaterial { + KeyMaterial([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, self.node_id]) } fn get_destination_script(&self) -> Script { @@ -184,6 +188,7 @@ impl KeysInterface for KeyProvider { let id = self.rand_bytes_id.fetch_add(1, atomic::Ordering::Relaxed); let keys = InMemorySigner::new( &secp_ctx, + self.get_node_secret(Recipient::Node).unwrap(), SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, self.node_id]).unwrap(), SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, self.node_id]).unwrap(), SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, self.node_id]).unwrap(), @@ -207,7 +212,7 @@ impl KeysInterface for KeyProvider { fn read_chan_signer(&self, buffer: &[u8]) -> Result { let mut reader = std::io::Cursor::new(buffer); - let inner: InMemorySigner = Readable::read(&mut reader)?; + let inner: InMemorySigner = ReadableArgs::read(&mut reader, self.get_node_secret(Recipient::Node).unwrap())?; let state = self.make_enforcement_state_cell(inner.commitment_seed); Ok(EnforcingSigner { @@ -217,7 +222,7 @@ impl KeysInterface for KeyProvider { }) } - fn sign_invoice(&self, _invoice_preimage: Vec) -> Result { + fn sign_invoice(&self, _hrp_bytes: &[u8], _invoice_data: &[u5], _recipient: Recipient) -> Result { unreachable!() } } @@ -284,7 +289,7 @@ fn get_payment_secret_hash(dest: &ChanMan, payment_id: &mut u8) -> Option<(Payme let mut payment_hash; for _ in 0..256 { payment_hash = PaymentHash(Sha256::hash(&[*payment_id; 1]).into_inner()); - if let Ok(payment_secret) = dest.create_inbound_payment_for_hash(payment_hash, None, 3600, 0) { + if let Ok(payment_secret) = dest.create_inbound_payment_for_hash(payment_hash, None, 3600) { return Some((payment_secret, payment_hash)); } *payment_id = payment_id.wrapping_add(1); @@ -305,7 +310,7 @@ fn send_payment(source: &ChanMan, dest: &ChanMan, dest_chan_id: u64, amt: u64, p fee_msat: amt, cltv_expiry_delta: 200, }]], - payee: None, + payment_params: None, }, payment_hash, &Some(payment_secret)) { check_payment_err(err); false @@ -331,7 +336,7 @@ fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, des fee_msat: amt, cltv_expiry_delta: 200, }]], - payee: None, + payment_params: None, }, payment_hash, &Some(payment_secret)) { check_payment_err(err); false @@ -339,7 +344,8 @@ fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, des } #[inline] -pub fn do_test(data: &[u8], out: Out) { +pub fn do_test(data: &[u8], underlying_out: Out) { + let out = SearchingOutput::new(underlying_out); let broadcast = Arc::new(TestBroadcaster{}); macro_rules! make_node { @@ -405,8 +411,8 @@ pub fn do_test(data: &[u8], out: Out) { let mut channel_txn = Vec::new(); macro_rules! make_channel { ($source: expr, $dest: expr, $chan_id: expr) => { { - $source.peer_connected(&$dest.get_our_node_id(), &Init { features: InitFeatures::known() }); - $dest.peer_connected(&$source.get_our_node_id(), &Init { features: InitFeatures::known() }); + $source.peer_connected(&$dest.get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None }); + $dest.peer_connected(&$source.get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None }); $source.create_channel($dest.get_our_node_id(), 100_000, 42, 0, None).unwrap(); let open_channel = { @@ -734,7 +740,11 @@ pub fn do_test(data: &[u8], out: Out) { // force-close which we should detect as an error). assert_eq!(msg.contents.flags & 2, 0); }, - _ => panic!("Unhandled message event {:?}", event), + _ => if out.may_fail.load(atomic::Ordering::Acquire) { + return; + } else { + panic!("Unhandled message event {:?}", event) + }, } if $limit_events != ProcessMessages::AllMessages { break; @@ -766,7 +776,11 @@ pub fn do_test(data: &[u8], out: Out) { events::MessageSendEvent::SendChannelUpdate { ref msg, .. } => { assert_eq!(msg.contents.flags & 2, 0); // The disable bit must never be set! }, - _ => panic!("Unhandled message event"), + _ => if out.may_fail.load(atomic::Ordering::Acquire) { + return; + } else { + panic!("Unhandled message event") + }, } } push_excess_b_events!(nodes[1].get_and_clear_pending_msg_events().drain(..), Some(0)); @@ -783,7 +797,11 @@ pub fn do_test(data: &[u8], out: Out) { events::MessageSendEvent::SendChannelUpdate { ref msg, .. } => { assert_eq!(msg.contents.flags & 2, 0); // The disable bit must never be set! }, - _ => panic!("Unhandled message event"), + _ => if out.may_fail.load(atomic::Ordering::Acquire) { + return; + } else { + panic!("Unhandled message event") + }, } } push_excess_b_events!(nodes[1].get_and_clear_pending_msg_events().drain(..), Some(2)); @@ -829,12 +847,17 @@ pub fn do_test(data: &[u8], out: Out) { } }, events::Event::PaymentSent { .. } => {}, + events::Event::PaymentPathSuccessful { .. } => {}, events::Event::PaymentPathFailed { .. } => {}, events::Event::PaymentForwarded { .. } if $node == 1 => {}, events::Event::PendingHTLCsForwardable { .. } => { nodes[$node].process_pending_htlc_forwards(); }, - _ => panic!("Unhandled event"), + _ => if out.may_fail.load(atomic::Ordering::Acquire) { + return; + } else { + panic!("Unhandled event") + }, } } had_events @@ -898,15 +921,15 @@ pub fn do_test(data: &[u8], out: Out) { }, 0x0e => { if chan_a_disconnected { - nodes[0].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known() }); - nodes[1].peer_connected(&nodes[0].get_our_node_id(), &Init { features: InitFeatures::known() }); + nodes[0].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None }); + nodes[1].peer_connected(&nodes[0].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None }); chan_a_disconnected = false; } }, 0x0f => { if chan_b_disconnected { - nodes[1].peer_connected(&nodes[2].get_our_node_id(), &Init { features: InitFeatures::known() }); - nodes[2].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known() }); + nodes[1].peer_connected(&nodes[2].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None }); + nodes[2].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None }); chan_b_disconnected = false; } }, @@ -1101,13 +1124,13 @@ pub fn do_test(data: &[u8], out: Out) { // Next, make sure peers are all connected to each other if chan_a_disconnected { - nodes[0].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known() }); - nodes[1].peer_connected(&nodes[0].get_our_node_id(), &Init { features: InitFeatures::known() }); + nodes[0].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None }); + nodes[1].peer_connected(&nodes[0].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None }); chan_a_disconnected = false; } if chan_b_disconnected { - nodes[1].peer_connected(&nodes[2].get_our_node_id(), &Init { features: InitFeatures::known() }); - nodes[2].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known() }); + nodes[1].peer_connected(&nodes[2].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None }); + nodes[2].peer_connected(&nodes[1].get_our_node_id(), &Init { features: InitFeatures::known(), remote_network_address: None }); chan_b_disconnected = false; } @@ -1125,7 +1148,7 @@ pub fn do_test(data: &[u8], out: Out) { break; } - // Finally, make sure that at least one end of each channel can make a substantial payment. + // Finally, make sure that at least one end of each channel can make a substantial payment assert!( send_payment(&nodes[0], &nodes[1], chan_a, 10_000_000, &mut payment_id) || send_payment(&nodes[1], &nodes[0], chan_a, 10_000_000, &mut payment_id)); @@ -1152,7 +1175,29 @@ pub fn do_test(data: &[u8], out: Out) { } } -pub fn chanmon_consistency_test(data: &[u8], out: Out) { +/// We actually have different behavior based on if a certain log string has been seen, so we have +/// to do a bit more tracking. +#[derive(Clone)] +struct SearchingOutput { + output: O, + may_fail: Arc, +} +impl Output for SearchingOutput { + fn locked_write(&self, data: &[u8]) { + // We hit a design limitation of LN state machine (see CONCURRENT_INBOUND_HTLC_FEE_BUFFER) + if std::str::from_utf8(data).unwrap().contains("Outbound update_fee HTLC buffer overflow - counterparty should force-close this channel") { + self.may_fail.store(true, atomic::Ordering::Release); + } + self.output.locked_write(data) + } +} +impl SearchingOutput { + pub fn new(output: O) -> Self { + Self { output, may_fail: Arc::new(atomic::AtomicBool::new(false)) } + } +} + +pub fn chanmon_consistency_test(data: &[u8], out: Out) { do_test(data, out); }