X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=fuzz%2Fsrc%2Fchanmon_consistency.rs;h=8afc2e15187edb5397474a64b2cf33e34359a667;hb=195e666953afef108ed4c47abba8b6414f8f15fc;hp=8919387818aef1b9f248634865e965a01cd98de0;hpb=2cae6f0ccb0e6b9e3d1c94359a906ddb4421be10;p=rust-lightning diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 89193878..b3cf867d 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -18,13 +18,11 @@ //! send-side handling is correct, other peers. We consider it a failure if any action results in a //! channel being force-closed. -use bitcoin::TxMerkleNode; -use bitcoin::blockdata::block::BlockHeader; use bitcoin::blockdata::constants::genesis_block; use bitcoin::blockdata::transaction::{Transaction, TxOut}; -use bitcoin::blockdata::script::{Builder, Script}; +use bitcoin::blockdata::script::{Builder, ScriptBuf}; use bitcoin::blockdata::opcodes; -use bitcoin::blockdata::locktime::PackedLockTime; +use bitcoin::blockdata::locktime::absolute::LockTime; use bitcoin::network::constants::Network; use bitcoin::hashes::Hash as TraitImport; @@ -32,21 +30,28 @@ use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::sha256d::Hash as Sha256dHash; use bitcoin::hash_types::{BlockHash, WPubkeyHash}; +use lightning::blinded_path::BlindedPath; +use lightning::blinded_path::payment::ReceiveTlvs; use lightning::chain; use lightning::chain::{BestBlock, ChannelMonitorUpdateStatus, chainmonitor, channelmonitor, Confirm, Watch}; use lightning::chain::channelmonitor::{ChannelMonitor, MonitorEvent}; use lightning::chain::transaction::OutPoint; use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; -use lightning::chain::keysinterface::{KeyMaterial, InMemorySigner, Recipient, EntropySource, NodeSigner, SignerProvider}; +use lightning::sign::{KeyMaterial, InMemorySigner, Recipient, EntropySource, NodeSigner, SignerProvider}; use lightning::events; use lightning::events::MessageSendEventsProvider; -use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; +use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret}; use lightning::ln::channelmanager::{ChainParameters, ChannelDetails, ChannelManager, PaymentSendFailure, ChannelManagerReadArgs, PaymentId, RecipientOnionFields}; use lightning::ln::channel::FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE; use lightning::ln::msgs::{self, CommitmentUpdate, ChannelMessageHandler, DecodeError, UpdateAddHTLC, Init}; use lightning::ln::script::ShutdownScript; -use lightning::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState}; +use lightning::ln::functional_test_utils::*; +use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice}; +use lightning::offers::invoice_request::UnsignedInvoiceRequest; +use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath}; +use lightning::util::test_channel_signer::{TestChannelSigner, EnforcementState}; use lightning::util::errors::APIError; +use lightning::util::hash_tables::*; use lightning::util::logger::Logger; use lightning::util::config::UserConfig; use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer}; @@ -55,13 +60,13 @@ use lightning::routing::router::{InFlightHtlcs, Path, Route, RouteHop, RoutePara use crate::utils::test_logger::{self, Output}; use crate::utils::test_persister::TestPersister; -use bitcoin::secp256k1::{Message, PublicKey, SecretKey, Scalar, Secp256k1}; +use bitcoin::secp256k1::{Message, PublicKey, SecretKey, Scalar, Secp256k1, self}; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; +use bitcoin::secp256k1::schnorr; use std::mem; use std::cmp::{self, Ordering}; -use hashbrown::{HashSet, hash_map, HashMap}; use std::sync::{Arc,Mutex}; use std::sync::atomic; use std::io::Cursor; @@ -78,9 +83,9 @@ impl FeeEstimator for FuzzEstimator { // always return a HighPriority feerate here which is >= the maximum Normal feerate and a // Background feerate which is <= the minimum Normal feerate. match conf_target { - ConfirmationTarget::HighPriority => MAX_FEE, - ConfirmationTarget::Background => 253, - ConfirmationTarget::Normal => cmp::min(self.ret_val.load(atomic::Ordering::Acquire), MAX_FEE), + ConfirmationTarget::OnChainSweep => MAX_FEE, + ConfirmationTarget::ChannelCloseMinimum|ConfirmationTarget::AnchorChannelFee|ConfirmationTarget::MinAllowedAnchorChannelRemoteFee|ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee|ConfirmationTarget::OutputSpendingFee => 253, + ConfirmationTarget::NonAnchorChannelFee => cmp::min(self.ret_val.load(atomic::Ordering::Acquire), MAX_FEE), } } } @@ -90,18 +95,39 @@ struct FuzzRouter {} impl Router for FuzzRouter { fn find_route( &self, _payer: &PublicKey, _params: &RouteParameters, _first_hops: Option<&[&ChannelDetails]>, - _inflight_htlcs: &InFlightHtlcs + _inflight_htlcs: InFlightHtlcs ) -> Result { Err(msgs::LightningError { err: String::from("Not implemented"), action: msgs::ErrorAction::IgnoreError }) } + + fn create_blinded_payment_paths( + &self, _recipient: PublicKey, _first_hops: Vec, _tlvs: ReceiveTlvs, + _amount_msats: u64, _secp_ctx: &Secp256k1, + ) -> Result, ()> { + unreachable!() + } +} + +impl MessageRouter for FuzzRouter { + fn find_path( + &self, _sender: PublicKey, _peers: Vec, _destination: Destination + ) -> Result { + unreachable!() + } + + fn create_blinded_paths( + &self, _recipient: PublicKey, _peers: Vec, _secp_ctx: &Secp256k1, + ) -> Result, ()> { + unreachable!() + } } pub struct TestBroadcaster {} impl BroadcasterInterface for TestBroadcaster { - fn broadcast_transaction(&self, _tx: &Transaction) { } + fn broadcast_transactions(&self, _txs: &[&Transaction]) { } } pub struct VecWriter(pub Vec); @@ -116,14 +142,13 @@ struct TestChainMonitor { pub logger: Arc, pub keys: Arc, pub persister: Arc, - pub chain_monitor: Arc, Arc, Arc, Arc, Arc>>, + pub chain_monitor: Arc, Arc, Arc, Arc, Arc>>, // If we reload a node with an old copy of ChannelMonitors, the ChannelManager deserialization // logic will automatically force-close our channels for us (as we don't have an up-to-date // monitor implying we are not able to punish misbehaving counterparties). Because this test // "fails" if we ever force-close a channel, we avoid doing so, always saving the latest // fully-serialized monitor state here, as well as the corresponding update_id. pub latest_monitors: Mutex)>>, - pub should_update_manager: atomic::AtomicBool, } impl TestChainMonitor { pub fn new(broadcaster: Arc, logger: Arc, feeest: Arc, persister: Arc, keys: Arc) -> Self { @@ -132,39 +157,33 @@ impl TestChainMonitor { logger, keys, persister, - latest_monitors: Mutex::new(HashMap::new()), - should_update_manager: atomic::AtomicBool::new(false), + latest_monitors: Mutex::new(new_hash_map()), } } } -impl chain::Watch for TestChainMonitor { - fn watch_channel(&self, funding_txo: OutPoint, monitor: channelmonitor::ChannelMonitor) -> chain::ChannelMonitorUpdateStatus { +impl chain::Watch for TestChainMonitor { + fn watch_channel(&self, funding_txo: OutPoint, monitor: channelmonitor::ChannelMonitor) -> Result { let mut ser = VecWriter(Vec::new()); monitor.write(&mut ser).unwrap(); if let Some(_) = self.latest_monitors.lock().unwrap().insert(funding_txo, (monitor.get_latest_update_id(), ser.0)) { panic!("Already had monitor pre-watch_channel"); } - self.should_update_manager.store(true, atomic::Ordering::Relaxed); self.chain_monitor.watch_channel(funding_txo, monitor) } fn update_channel(&self, funding_txo: OutPoint, update: &channelmonitor::ChannelMonitorUpdate) -> chain::ChannelMonitorUpdateStatus { let mut map_lock = self.latest_monitors.lock().unwrap(); - let mut map_entry = match map_lock.entry(funding_txo) { - hash_map::Entry::Occupied(entry) => entry, - hash_map::Entry::Vacant(_) => panic!("Didn't have monitor on update call"), - }; - let deserialized_monitor = <(BlockHash, channelmonitor::ChannelMonitor)>:: - read(&mut Cursor::new(&map_entry.get().1), (&*self.keys, &*self.keys)).unwrap().1; - deserialized_monitor.update_monitor(update, &&TestBroadcaster{}, &FuzzEstimator { ret_val: atomic::AtomicU32::new(253) }, &self.logger).unwrap(); + let map_entry = map_lock.get_mut(&funding_txo).expect("Didn't have monitor on update call"); + let deserialized_monitor = <(BlockHash, channelmonitor::ChannelMonitor)>:: + read(&mut Cursor::new(&map_entry.1), (&*self.keys, &*self.keys)).unwrap().1; + deserialized_monitor.update_monitor(update, &&TestBroadcaster{}, &&FuzzEstimator { ret_val: atomic::AtomicU32::new(253) }, &self.logger).unwrap(); let mut ser = VecWriter(Vec::new()); deserialized_monitor.write(&mut ser).unwrap(); - map_entry.insert((update.update_id, ser.0)); - self.should_update_manager.store(true, atomic::Ordering::Relaxed); + *map_entry = (update.update_id, ser.0); self.chain_monitor.update_channel(funding_txo, update) } - fn release_pending_monitor_events(&self) -> Vec<(OutPoint, Vec, Option)> { + fn release_pending_monitor_events(&self) -> Vec<(OutPoint, ChannelId, Vec, Option)> { return self.chain_monitor.release_pending_monitor_events(); } } @@ -212,6 +231,18 @@ impl NodeSigner for KeyProvider { unreachable!() } + fn sign_bolt12_invoice_request( + &self, _invoice_request: &UnsignedInvoiceRequest + ) -> Result { + unreachable!() + } + + fn sign_bolt12_invoice( + &self, _invoice: &UnsignedBolt12Invoice, + ) -> Result { + unreachable!() + } + fn sign_gossip_message(&self, msg: lightning::ln::msgs::UnsignedGossipMessage) -> Result { let msg_hash = Message::from_slice(&Sha256dHash::hash(&msg.encode()[..])[..]).map_err(|_| ())?; let secp_ctx = Secp256k1::signing_only(); @@ -220,14 +251,16 @@ impl NodeSigner for KeyProvider { } impl SignerProvider for KeyProvider { - type Signer = EnforcingSigner; + type EcdsaSigner = TestChannelSigner; + #[cfg(taproot)] + type TaprootSigner = TestChannelSigner; fn generate_channel_keys_id(&self, _inbound: bool, _channel_value_satoshis: u64, _user_channel_id: u128) -> [u8; 32] { let id = self.rand_bytes_id.fetch_add(1, atomic::Ordering::Relaxed) as u8; [id; 32] } - fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::Signer { + fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> Self::EcdsaSigner { let secp_ctx = Secp256k1::signing_only(); let id = channel_keys_id[0]; let keys = InMemorySigner::new( @@ -243,27 +276,28 @@ impl SignerProvider for KeyProvider { channel_keys_id, ); let revoked_commitment = self.make_enforcement_state_cell(keys.commitment_seed); - EnforcingSigner::new_with_revoked(keys, revoked_commitment, false) + TestChannelSigner::new_with_revoked(keys, revoked_commitment, false) } - fn read_chan_signer(&self, buffer: &[u8]) -> Result { + fn read_chan_signer(&self, buffer: &[u8]) -> Result { let mut reader = std::io::Cursor::new(buffer); let inner: InMemorySigner = ReadableArgs::read(&mut reader, self)?; let state = self.make_enforcement_state_cell(inner.commitment_seed); - Ok(EnforcingSigner { + Ok(TestChannelSigner { inner, state, disable_revocation_policy_check: false, + available: Arc::new(Mutex::new(true)), }) } - fn get_destination_script(&self) -> Result { + fn get_destination_script(&self, _channel_keys_id: [u8; 32]) -> Result { let secp_ctx = Secp256k1::signing_only(); let channel_monitor_claim_key = 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, 2, self.node_secret[31]]).unwrap(); let our_channel_monitor_claim_key_hash = WPubkeyHash::hash(&PublicKey::from_secret_key(&secp_ctx, &channel_monitor_claim_key).serialize()); - Ok(Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&our_channel_monitor_claim_key_hash[..]).into_script()) + Ok(Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(our_channel_monitor_claim_key_hash).into_script()) } fn get_shutdown_scriptpubkey(&self) -> Result { @@ -286,7 +320,7 @@ impl KeyProvider { } #[inline] -fn check_api_err(api_err: APIError) { +fn check_api_err(api_err: APIError, sendable_bounds_violated: bool) { match api_err { APIError::APIMisuseError { .. } => panic!("We can't misuse the API"), APIError::FeeRateTooHigh { .. } => panic!("We can't send too much fee?"), @@ -297,15 +331,11 @@ fn check_api_err(api_err: APIError) { // is probably just stale and you should add new messages here. match err.as_str() { "Peer for first hop currently disconnected" => {}, - _ if err.starts_with("Cannot push more than their max accepted HTLCs ") => {}, - _ if err.starts_with("Cannot send value that would put us over the max HTLC value in flight our peer will accept ") => {}, - _ if err.starts_with("Cannot send value that would put our balance under counterparty-announced channel reserve value") => {}, - _ if err.starts_with("Cannot send value that would put counterparty balance under holder-announced channel reserve value") => {}, - _ if err.starts_with("Cannot send value that would overdraw remaining funds.") => {}, - _ if err.starts_with("Cannot send value that would not leave enough to pay for fees.") => {}, - _ if err.starts_with("Cannot send value that would put our exposure to dust HTLCs at") => {}, + _ if err.starts_with("Cannot send less than our next-HTLC minimum - ") => {}, + _ if err.starts_with("Cannot send more than our next-HTLC maximum - ") => {}, _ => panic!("{}", err), } + assert!(sendable_bounds_violated); }, APIError::MonitorUpdateInProgress => { // We can (obviously) temp-fail a monitor update @@ -314,17 +344,17 @@ fn check_api_err(api_err: APIError) { } } #[inline] -fn check_payment_err(send_err: PaymentSendFailure) { +fn check_payment_err(send_err: PaymentSendFailure, sendable_bounds_violated: bool) { match send_err { - PaymentSendFailure::ParameterError(api_err) => check_api_err(api_err), + PaymentSendFailure::ParameterError(api_err) => check_api_err(api_err, sendable_bounds_violated), PaymentSendFailure::PathParameterError(per_path_results) => { - for res in per_path_results { if let Err(api_err) = res { check_api_err(api_err); } } + for res in per_path_results { if let Err(api_err) = res { check_api_err(api_err, sendable_bounds_violated); } } }, PaymentSendFailure::AllFailedResendSafe(per_path_results) => { - for api_err in per_path_results { check_api_err(api_err); } + for api_err in per_path_results { check_api_err(api_err, sendable_bounds_violated); } }, PaymentSendFailure::PartialFailure { results, .. } => { - for res in results { if let Err(api_err) = res { check_api_err(api_err); } } + for res in results { if let Err(api_err) = res { check_api_err(api_err, sendable_bounds_violated); } } }, PaymentSendFailure::DuplicatePayment => panic!(), } @@ -336,7 +366,7 @@ type ChanMan<'a> = ChannelManager, Arc, A fn get_payment_secret_hash(dest: &ChanMan, payment_id: &mut u8) -> Option<(PaymentSecret, PaymentHash)> { let mut payment_hash; for _ in 0..256 { - payment_hash = PaymentHash(Sha256::hash(&[*payment_id; 1]).into_inner()); + payment_hash = PaymentHash(Sha256::hash(&[*payment_id; 1]).to_byte_array()); if let Ok(payment_secret) = dest.create_inbound_payment_for_hash(payment_hash, None, 3600, None) { return Some((payment_secret, payment_hash)); } @@ -352,6 +382,11 @@ fn send_payment(source: &ChanMan, dest: &ChanMan, dest_chan_id: u64, amt: u64, p let mut payment_id = [0; 32]; payment_id[0..8].copy_from_slice(&payment_idx.to_ne_bytes()); *payment_idx += 1; + let (min_value_sendable, max_value_sendable) = source.list_usable_channels() + .iter().find(|chan| chan.short_channel_id == Some(dest_chan_id)) + .map(|chan| + (chan.next_outbound_htlc_minimum_msat, chan.next_outbound_htlc_limit_msat)) + .unwrap_or((0, 0)); if let Err(err) = source.send_payment_with_route(&Route { paths: vec![Path { hops: vec![RouteHop { pubkey: dest.get_our_node_id(), @@ -360,12 +395,19 @@ fn send_payment(source: &ChanMan, dest: &ChanMan, dest_chan_id: u64, amt: u64, p channel_features: dest.channel_features(), fee_msat: amt, cltv_expiry_delta: 200, + maybe_announced_channel: true, }], blinded_tail: None }], - payment_params: None, + route_params: None, }, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_id)) { - check_payment_err(err); + check_payment_err(err, amt > max_value_sendable || amt < min_value_sendable); false - } else { true } + } else { + // Note that while the max is a strict upper-bound, we can occasionally send substantially + // below the minimum, with some gap which is unusable immediately below the minimum. Thus, + // we don't check against min_value_sendable here. + assert!(amt <= max_value_sendable); + true + } } #[inline] fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, dest: &ChanMan, dest_chan_id: u64, amt: u64, payment_id: &mut u8, payment_idx: &mut u64) -> bool { @@ -374,31 +416,46 @@ fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, des let mut payment_id = [0; 32]; payment_id[0..8].copy_from_slice(&payment_idx.to_ne_bytes()); *payment_idx += 1; + let (min_value_sendable, max_value_sendable) = source.list_usable_channels() + .iter().find(|chan| chan.short_channel_id == Some(middle_chan_id)) + .map(|chan| + (chan.next_outbound_htlc_minimum_msat, chan.next_outbound_htlc_limit_msat)) + .unwrap_or((0, 0)); + let first_hop_fee = 50_000; if let Err(err) = source.send_payment_with_route(&Route { paths: vec![Path { hops: vec![RouteHop { pubkey: middle.get_our_node_id(), node_features: middle.node_features(), short_channel_id: middle_chan_id, channel_features: middle.channel_features(), - fee_msat: 50000, + fee_msat: first_hop_fee, cltv_expiry_delta: 100, - },RouteHop { + maybe_announced_channel: true, + }, RouteHop { pubkey: dest.get_our_node_id(), node_features: dest.node_features(), short_channel_id: dest_chan_id, channel_features: dest.channel_features(), fee_msat: amt, cltv_expiry_delta: 200, + maybe_announced_channel: true, }], blinded_tail: None }], - payment_params: None, + route_params: None, }, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_id)) { - check_payment_err(err); + let sent_amt = amt + first_hop_fee; + check_payment_err(err, sent_amt < min_value_sendable || sent_amt > max_value_sendable); false - } else { true } + } else { + // Note that while the max is a strict upper-bound, we can occasionally send substantially + // below the minimum, with some gap which is unusable immediately below the minimum. Thus, + // we don't check against min_value_sendable here. + assert!(amt + first_hop_fee <= max_value_sendable); + true + } } #[inline] -pub fn do_test(data: &[u8], underlying_out: Out) { +pub fn do_test(data: &[u8], underlying_out: Out, anchors: bool) { let out = SearchingOutput::new(underlying_out); let broadcast = Arc::new(TestBroadcaster{}); let router = FuzzRouter {}; @@ -407,7 +464,7 @@ pub fn do_test(data: &[u8], underlying_out: Out) { ($node_id: expr, $fee_estimator: expr) => { { let logger: Arc = Arc::new(test_logger::TestLogger::new($node_id.to_string(), out.clone())); let node_secret = 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, $node_id]).unwrap(); - let keys_manager = Arc::new(KeyProvider { node_secret, rand_bytes_id: atomic::AtomicU32::new(0), enforcement_states: Mutex::new(HashMap::new()) }); + let keys_manager = Arc::new(KeyProvider { node_secret, rand_bytes_id: atomic::AtomicU32::new(0), enforcement_states: Mutex::new(new_hash_map()) }); let monitor = Arc::new(TestChainMonitor::new(broadcast.clone(), logger.clone(), $fee_estimator.clone(), Arc::new(TestPersister { update_ret: Mutex::new(ChannelMonitorUpdateStatus::Completed) @@ -416,12 +473,17 @@ pub fn do_test(data: &[u8], underlying_out: Out) { let mut config = UserConfig::default(); config.channel_config.forwarding_fee_proportional_millionths = 0; config.channel_handshake_config.announced_channel = true; + if anchors { + config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + config.manually_accept_inbound_channels = true; + } let network = Network::Bitcoin; + let best_block_timestamp = genesis_block(network).header.time; let params = ChainParameters { network, best_block: BestBlock::from_network(network), }; - (ChannelManager::new($fee_estimator.clone(), monitor.clone(), broadcast.clone(), &router, Arc::clone(&logger), keys_manager.clone(), keys_manager.clone(), keys_manager.clone(), config, params), + (ChannelManager::new($fee_estimator.clone(), monitor.clone(), broadcast.clone(), &router, Arc::clone(&logger), keys_manager.clone(), keys_manager.clone(), keys_manager.clone(), config, params, best_block_timestamp), monitor, keys_manager) } } } @@ -438,14 +500,18 @@ pub fn do_test(data: &[u8], underlying_out: Out) { let mut config = UserConfig::default(); config.channel_config.forwarding_fee_proportional_millionths = 0; config.channel_handshake_config.announced_channel = true; + if anchors { + config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; + config.manually_accept_inbound_channels = true; + } - let mut monitors = HashMap::new(); + let mut monitors = new_hash_map(); let mut old_monitors = $old_monitors.latest_monitors.lock().unwrap(); for (outpoint, (update_id, monitor_ser)) in old_monitors.drain() { - monitors.insert(outpoint, <(BlockHash, ChannelMonitor)>::read(&mut Cursor::new(&monitor_ser), (&*$keys_manager, &*$keys_manager)).expect("Failed to read monitor").1); + monitors.insert(outpoint, <(BlockHash, ChannelMonitor)>::read(&mut Cursor::new(&monitor_ser), (&*$keys_manager, &*$keys_manager)).expect("Failed to read monitor").1); chain_monitor.latest_monitors.lock().unwrap().insert(outpoint, (update_id, monitor_ser)); } - let mut monitor_refs = HashMap::new(); + let mut monitor_refs = new_hash_map(); for (outpoint, monitor) in monitors.iter_mut() { monitor_refs.insert(*outpoint, monitor); } @@ -466,7 +532,7 @@ pub fn do_test(data: &[u8], underlying_out: Out) { let res = (<(BlockHash, ChanMan)>::read(&mut Cursor::new(&$ser.0), read_args).expect("Failed to read manager").1, chain_monitor.clone()); for (funding_txo, mon) in monitors.drain() { assert_eq!(chain_monitor.chain_monitor.watch_channel(funding_txo, mon), - ChannelMonitorUpdateStatus::Completed); + Ok(ChannelMonitorUpdateStatus::Completed)); } res } } @@ -474,11 +540,15 @@ pub fn do_test(data: &[u8], underlying_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: $dest.init_features(), remote_network_address: None }, true).unwrap(); - $dest.peer_connected(&$source.get_our_node_id(), &Init { features: $source.init_features(), remote_network_address: None }, false).unwrap(); - - $source.create_channel($dest.get_our_node_id(), 100_000, 42, 0, None).unwrap(); + ($source: expr, $dest: expr, $dest_keys_manager: expr, $chan_id: expr) => { { + $source.peer_connected(&$dest.get_our_node_id(), &Init { + features: $dest.init_features(), networks: None, remote_network_address: None + }, true).unwrap(); + $dest.peer_connected(&$source.get_our_node_id(), &Init { + features: $source.init_features(), networks: None, remote_network_address: None + }, false).unwrap(); + + $source.create_channel($dest.get_our_node_id(), 100_000, 42, 0, None, None).unwrap(); let open_channel = { let events = $source.get_and_clear_pending_msg_events(); assert_eq!(events.len(), 1); @@ -489,6 +559,22 @@ pub fn do_test(data: &[u8], underlying_out: Out) { $dest.handle_open_channel(&$source.get_our_node_id(), &open_channel); let accept_channel = { + if anchors { + let events = $dest.get_and_clear_pending_events(); + assert_eq!(events.len(), 1); + if let events::Event::OpenChannelRequest { + ref temporary_channel_id, ref counterparty_node_id, .. + } = events[0] { + let mut random_bytes = [0u8; 16]; + random_bytes.copy_from_slice(&$dest_keys_manager.get_secure_random_bytes()[..16]); + let user_channel_id = u128::from_be_bytes(random_bytes); + $dest.accept_inbound_channel( + temporary_channel_id, + counterparty_node_id, + user_channel_id, + ).unwrap(); + } else { panic!("Wrong event type"); } + } let events = $dest.get_and_clear_pending_msg_events(); assert_eq!(events.len(), 1); if let events::MessageSendEvent::SendAcceptChannel { ref msg, .. } = events[0] { @@ -502,7 +588,7 @@ pub fn do_test(data: &[u8], underlying_out: Out) { let events = $source.get_and_clear_pending_events(); assert_eq!(events.len(), 1); if let events::Event::FundingGenerationReady { ref temporary_channel_id, ref channel_value_satoshis, ref output_script, .. } = events[0] { - let tx = Transaction { version: $chan_id, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: vec![TxOut { + let tx = Transaction { version: $chan_id, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut { value: *channel_value_satoshis, script_pubkey: output_script.clone(), }]}; funding_output = OutPoint { txid: tx.txid(), index: 0 }; @@ -547,11 +633,11 @@ pub fn do_test(data: &[u8], underlying_out: Out) { macro_rules! confirm_txn { ($node: expr) => { { let chain_hash = genesis_block(Network::Bitcoin).block_hash(); - let mut header = BlockHeader { version: 0x20000000, prev_blockhash: chain_hash, merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 }; + let mut header = create_dummy_header(chain_hash, 42); let txdata: Vec<_> = channel_txn.iter().enumerate().map(|(i, tx)| (i + 1, tx)).collect(); $node.transactions_confirmed(&header, &txdata, 1); for _ in 2..100 { - header = BlockHeader { version: 0x20000000, prev_blockhash: header.block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 }; + header = create_dummy_header(header.block_hash(), 42); } $node.best_block_updated(&header, 99); } } @@ -600,8 +686,8 @@ pub fn do_test(data: &[u8], underlying_out: Out) { let mut nodes = [node_a, node_b, node_c]; - let chan_1_funding = make_channel!(nodes[0], nodes[1], 0); - let chan_2_funding = make_channel!(nodes[1], nodes[2], 1); + let chan_1_funding = make_channel!(nodes[0], nodes[1], keys_manager_b, 0); + let chan_2_funding = make_channel!(nodes[1], nodes[2], keys_manager_c, 1); for node in nodes.iter() { confirm_txn!(node); @@ -892,7 +978,7 @@ pub fn do_test(data: &[u8], underlying_out: Out) { // In case we get 256 payments we may have a hash collision, resulting in the // second claim/fail call not finding the duplicate-hash HTLC, so we have to // deduplicate the calls here. - let mut claim_set = HashSet::new(); + let mut claim_set = new_hash_map(); let mut events = nodes[$node].get_and_clear_pending_events(); // Sort events so that PendingHTLCsForwardable get processed last. This avoids a // case where we first process a PendingHTLCsForwardable, then claim/fail on a @@ -914,7 +1000,7 @@ pub fn do_test(data: &[u8], underlying_out: Out) { for event in events.drain(..) { match event { events::Event::PaymentClaimable { payment_hash, .. } => { - if claim_set.insert(payment_hash.0) { + if claim_set.insert(payment_hash.0, ()).is_none() { if $fail { nodes[$node].fail_htlc_backwards(&payment_hash); } else { @@ -1007,15 +1093,23 @@ pub fn do_test(data: &[u8], underlying_out: Out) { }, 0x0e => { if chan_a_disconnected { - nodes[0].peer_connected(&nodes[1].get_our_node_id(), &Init { features: nodes[1].init_features(), remote_network_address: None }, true).unwrap(); - nodes[1].peer_connected(&nodes[0].get_our_node_id(), &Init { features: nodes[0].init_features(), remote_network_address: None }, false).unwrap(); + nodes[0].peer_connected(&nodes[1].get_our_node_id(), &Init { + features: nodes[1].init_features(), networks: None, remote_network_address: None + }, true).unwrap(); + nodes[1].peer_connected(&nodes[0].get_our_node_id(), &Init { + features: nodes[0].init_features(), networks: None, remote_network_address: None + }, false).unwrap(); chan_a_disconnected = false; } }, 0x0f => { if chan_b_disconnected { - nodes[1].peer_connected(&nodes[2].get_our_node_id(), &Init { features: nodes[2].init_features(), remote_network_address: None }, true).unwrap(); - nodes[2].peer_connected(&nodes[1].get_our_node_id(), &Init { features: nodes[1].init_features(), remote_network_address: None }, false).unwrap(); + nodes[1].peer_connected(&nodes[2].get_our_node_id(), &Init { + features: nodes[2].init_features(), networks: None, remote_network_address: None + }, true).unwrap(); + nodes[2].peer_connected(&nodes[1].get_our_node_id(), &Init { + features: nodes[1].init_features(), networks: None, remote_network_address: None + }, false).unwrap(); chan_b_disconnected = false; } }, @@ -1054,11 +1148,9 @@ pub fn do_test(data: &[u8], underlying_out: Out) { if !chan_a_disconnected { nodes[1].peer_disconnected(&nodes[0].get_our_node_id()); chan_a_disconnected = true; - drain_msg_events_on_disconnect!(0); - } - if monitor_a.should_update_manager.load(atomic::Ordering::Relaxed) { - node_a_ser.0.clear(); - nodes[0].write(&mut node_a_ser).unwrap(); + push_excess_b_events!(nodes[1].get_and_clear_pending_msg_events().drain(..), Some(0)); + ab_events.clear(); + ba_events.clear(); } let (new_node_a, new_monitor_a) = reload_node!(node_a_ser, 0, monitor_a, keys_manager_a, fee_est_a); nodes[0] = new_node_a; @@ -1087,11 +1179,9 @@ pub fn do_test(data: &[u8], underlying_out: Out) { if !chan_b_disconnected { nodes[1].peer_disconnected(&nodes[2].get_our_node_id()); chan_b_disconnected = true; - drain_msg_events_on_disconnect!(2); - } - if monitor_c.should_update_manager.load(atomic::Ordering::Relaxed) { - node_c_ser.0.clear(); - nodes[2].write(&mut node_c_ser).unwrap(); + push_excess_b_events!(nodes[1].get_and_clear_pending_msg_events().drain(..), Some(2)); + bc_events.clear(); + cb_events.clear(); } let (new_node_c, new_monitor_c) = reload_node!(node_c_ser, 2, monitor_c, keys_manager_c, fee_est_c); nodes[2] = new_node_c; @@ -1156,7 +1246,10 @@ pub fn do_test(data: &[u8], underlying_out: Out) { 0x6d => { send_hop_payment(&nodes[2], &nodes[1], chan_b, &nodes[0], chan_a, 1, &mut payment_id, &mut payment_idx); }, 0x80 => { - let max_feerate = last_htlc_clear_fee_a * FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32; + let mut max_feerate = last_htlc_clear_fee_a; + if !anchors { + max_feerate *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32; + } if fee_est_a.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250 > max_feerate { fee_est_a.ret_val.store(max_feerate, atomic::Ordering::Release); } @@ -1165,7 +1258,10 @@ pub fn do_test(data: &[u8], underlying_out: Out) { 0x81 => { fee_est_a.ret_val.store(253, atomic::Ordering::Release); nodes[0].maybe_update_chan_fees(); }, 0x84 => { - let max_feerate = last_htlc_clear_fee_b * FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32; + let mut max_feerate = last_htlc_clear_fee_b; + if !anchors { + max_feerate *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32; + } if fee_est_b.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250 > max_feerate { fee_est_b.ret_val.store(max_feerate, atomic::Ordering::Release); } @@ -1174,7 +1270,10 @@ pub fn do_test(data: &[u8], underlying_out: Out) { 0x85 => { fee_est_b.ret_val.store(253, atomic::Ordering::Release); nodes[1].maybe_update_chan_fees(); }, 0x88 => { - let max_feerate = last_htlc_clear_fee_c * FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32; + let mut max_feerate = last_htlc_clear_fee_c; + if !anchors { + max_feerate *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32; + } if fee_est_c.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250 > max_feerate { fee_est_c.ret_val.store(max_feerate, atomic::Ordering::Release); } @@ -1182,6 +1281,94 @@ pub fn do_test(data: &[u8], underlying_out: Out) { }, 0x89 => { fee_est_c.ret_val.store(253, atomic::Ordering::Release); nodes[2].maybe_update_chan_fees(); }, + 0xf0 => { + let pending_updates = monitor_a.chain_monitor.list_pending_monitor_updates().remove(&chan_1_funding).unwrap(); + if let Some(id) = pending_updates.get(0) { + monitor_a.chain_monitor.channel_monitor_updated(chan_1_funding, *id).unwrap(); + } + nodes[0].process_monitor_events(); + } + 0xf1 => { + let pending_updates = monitor_a.chain_monitor.list_pending_monitor_updates().remove(&chan_1_funding).unwrap(); + if let Some(id) = pending_updates.get(1) { + monitor_a.chain_monitor.channel_monitor_updated(chan_1_funding, *id).unwrap(); + } + nodes[0].process_monitor_events(); + } + 0xf2 => { + let pending_updates = monitor_a.chain_monitor.list_pending_monitor_updates().remove(&chan_1_funding).unwrap(); + if let Some(id) = pending_updates.last() { + monitor_a.chain_monitor.channel_monitor_updated(chan_1_funding, *id).unwrap(); + } + nodes[0].process_monitor_events(); + } + + 0xf4 => { + let pending_updates = monitor_b.chain_monitor.list_pending_monitor_updates().remove(&chan_1_funding).unwrap(); + if let Some(id) = pending_updates.get(0) { + monitor_b.chain_monitor.channel_monitor_updated(chan_1_funding, *id).unwrap(); + } + nodes[1].process_monitor_events(); + } + 0xf5 => { + let pending_updates = monitor_b.chain_monitor.list_pending_monitor_updates().remove(&chan_1_funding).unwrap(); + if let Some(id) = pending_updates.get(1) { + monitor_b.chain_monitor.channel_monitor_updated(chan_1_funding, *id).unwrap(); + } + nodes[1].process_monitor_events(); + } + 0xf6 => { + let pending_updates = monitor_b.chain_monitor.list_pending_monitor_updates().remove(&chan_1_funding).unwrap(); + if let Some(id) = pending_updates.last() { + monitor_b.chain_monitor.channel_monitor_updated(chan_1_funding, *id).unwrap(); + } + nodes[1].process_monitor_events(); + } + + 0xf8 => { + let pending_updates = monitor_b.chain_monitor.list_pending_monitor_updates().remove(&chan_2_funding).unwrap(); + if let Some(id) = pending_updates.get(0) { + monitor_b.chain_monitor.channel_monitor_updated(chan_2_funding, *id).unwrap(); + } + nodes[1].process_monitor_events(); + } + 0xf9 => { + let pending_updates = monitor_b.chain_monitor.list_pending_monitor_updates().remove(&chan_2_funding).unwrap(); + if let Some(id) = pending_updates.get(1) { + monitor_b.chain_monitor.channel_monitor_updated(chan_2_funding, *id).unwrap(); + } + nodes[1].process_monitor_events(); + } + 0xfa => { + let pending_updates = monitor_b.chain_monitor.list_pending_monitor_updates().remove(&chan_2_funding).unwrap(); + if let Some(id) = pending_updates.last() { + monitor_b.chain_monitor.channel_monitor_updated(chan_2_funding, *id).unwrap(); + } + nodes[1].process_monitor_events(); + } + + 0xfc => { + let pending_updates = monitor_c.chain_monitor.list_pending_monitor_updates().remove(&chan_2_funding).unwrap(); + if let Some(id) = pending_updates.get(0) { + monitor_c.chain_monitor.channel_monitor_updated(chan_2_funding, *id).unwrap(); + } + nodes[2].process_monitor_events(); + } + 0xfd => { + let pending_updates = monitor_c.chain_monitor.list_pending_monitor_updates().remove(&chan_2_funding).unwrap(); + if let Some(id) = pending_updates.get(1) { + monitor_c.chain_monitor.channel_monitor_updated(chan_2_funding, *id).unwrap(); + } + nodes[2].process_monitor_events(); + } + 0xfe => { + let pending_updates = monitor_c.chain_monitor.list_pending_monitor_updates().remove(&chan_2_funding).unwrap(); + if let Some(id) = pending_updates.last() { + monitor_c.chain_monitor.channel_monitor_updated(chan_2_funding, *id).unwrap(); + } + nodes[2].process_monitor_events(); + } + 0xff => { // Test that no channel is in a stuck state where neither party can send funds even // after we resolve all pending events. @@ -1210,13 +1397,21 @@ pub fn do_test(data: &[u8], underlying_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: nodes[1].init_features(), remote_network_address: None }, true).unwrap(); - nodes[1].peer_connected(&nodes[0].get_our_node_id(), &Init { features: nodes[0].init_features(), remote_network_address: None }, false).unwrap(); + nodes[0].peer_connected(&nodes[1].get_our_node_id(), &Init { + features: nodes[1].init_features(), networks: None, remote_network_address: None + }, true).unwrap(); + nodes[1].peer_connected(&nodes[0].get_our_node_id(), &Init { + features: nodes[0].init_features(), networks: None, remote_network_address: None + }, false).unwrap(); chan_a_disconnected = false; } if chan_b_disconnected { - nodes[1].peer_connected(&nodes[2].get_our_node_id(), &Init { features: nodes[2].init_features(), remote_network_address: None }, true).unwrap(); - nodes[2].peer_connected(&nodes[1].get_our_node_id(), &Init { features: nodes[1].init_features(), remote_network_address: None }, false).unwrap(); + nodes[1].peer_connected(&nodes[2].get_our_node_id(), &Init { + features: nodes[2].init_features(), networks: None, remote_network_address: None + }, true).unwrap(); + nodes[2].peer_connected(&nodes[1].get_our_node_id(), &Init { + features: nodes[1].init_features(), networks: None, remote_network_address: None + }, false).unwrap(); chan_b_disconnected = false; } @@ -1249,15 +1444,18 @@ pub fn do_test(data: &[u8], underlying_out: Out) { _ => test_return!(), } - node_a_ser.0.clear(); - nodes[0].write(&mut node_a_ser).unwrap(); - monitor_a.should_update_manager.store(false, atomic::Ordering::Relaxed); - node_b_ser.0.clear(); - nodes[1].write(&mut node_b_ser).unwrap(); - monitor_b.should_update_manager.store(false, atomic::Ordering::Relaxed); - node_c_ser.0.clear(); - nodes[2].write(&mut node_c_ser).unwrap(); - monitor_c.should_update_manager.store(false, atomic::Ordering::Relaxed); + if nodes[0].get_and_clear_needs_persistence() == true { + node_a_ser.0.clear(); + nodes[0].write(&mut node_a_ser).unwrap(); + } + if nodes[1].get_and_clear_needs_persistence() == true { + node_b_ser.0.clear(); + nodes[1].write(&mut node_b_ser).unwrap(); + } + if nodes[2].get_and_clear_needs_persistence() == true { + node_c_ser.0.clear(); + nodes[2].write(&mut node_c_ser).unwrap(); + } } } @@ -1284,10 +1482,12 @@ impl SearchingOutput { } pub fn chanmon_consistency_test(data: &[u8], out: Out) { - do_test(data, out); + do_test(data, out.clone(), false); + do_test(data, out, true); } #[no_mangle] pub extern "C" fn chanmon_consistency_run(data: *const u8, datalen: usize) { - do_test(unsafe { std::slice::from_raw_parts(data, datalen) }, test_logger::DevNull{}); + do_test(unsafe { std::slice::from_raw_parts(data, datalen) }, test_logger::DevNull{}, false); + do_test(unsafe { std::slice::from_raw_parts(data, datalen) }, test_logger::DevNull{}, true); }