diff --git a/Cargo.toml b/Cargo.toml index 41a91996f7c..5f19f395a09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ members = [ exclude = [ "lightning-transaction-sync", + "lightning-tests", "ext-functional-test-demo", "no-std-check", "msrv-no-dev-deps-check", diff --git a/ci/ci-tests.sh b/ci/ci-tests.sh index a5c6d828ab0..5d02bd6c362 100755 --- a/ci/ci-tests.sh +++ b/ci/ci-tests.sh @@ -50,9 +50,17 @@ WORKSPACE_MEMBERS=( possiblyrandom ) -echo -e "\n\nChecking, testing, and building docs for all workspace members individually..." +echo -e "\n\nTesting the workspace, except lightning-transaction-sync." +cargo test --verbose --color always + +echo -e "\n\nTesting upgrade from prior versions of LDK" +pushd lightning-tests +[ "$RUSTC_MINOR_VERSION" -lt 65 ] && cargo update -p regex --precise "1.9.6" --verbose +cargo test +popd + +echo -e "\n\nChecking and building docs for all workspace members individually..." for DIR in "${WORKSPACE_MEMBERS[@]}"; do - cargo test -p "$DIR" --verbose --color always cargo check -p "$DIR" --verbose --color always cargo doc -p "$DIR" --document-private-items done diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 8b41c53a635..7c3700f85f5 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -380,7 +380,7 @@ impl SignerProvider for KeyProvider { ); let revoked_commitment = self.make_enforcement_state_cell(keys.commitment_seed); let keys = DynSigner::new(keys); - TestChannelSigner::new_with_revoked(keys, revoked_commitment, false) + TestChannelSigner::new_with_revoked(keys, revoked_commitment, false, false) } fn get_destination_script(&self, _channel_keys_id: [u8; 32]) -> Result { diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 5281a933526..4291534a868 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -443,85 +443,29 @@ impl SignerProvider for KeyProvider { [ctr; 32] } - fn derive_channel_signer(&self, channel_keys_id: [u8; 32]) -> Self::EcdsaSigner { + fn derive_channel_signer(&self, keys_id: [u8; 32]) -> Self::EcdsaSigner { let secp_ctx = Secp256k1::signing_only(); - let ctr = channel_keys_id[0]; + let ctr = keys_id[0]; let (inbound, state) = self.signer_state.borrow().get(&ctr).unwrap().clone(); - TestChannelSigner::new_with_revoked( - DynSigner::new(if inbound { - InMemorySigner::new( - &secp_ctx, - 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, ctr, - ]) - .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, 2, ctr, - ]) - .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, 3, ctr, - ]) - .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, ctr, - ]) - .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, ctr, - ]) - .unwrap(), - [ - 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, ctr, - ], - channel_keys_id, - channel_keys_id, - ) - } else { - InMemorySigner::new( - &secp_ctx, - 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, 7, ctr, - ]) - .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, 8, ctr, - ]) - .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, 9, ctr, - ]) - .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, 10, ctr, - ]) - .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, 11, ctr, - ]) - .unwrap(), - [ - 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, 12, ctr, - ], - channel_keys_id, - channel_keys_id, - ) - }), - state, - false, - ) + + let (a, b, c, d, e, f); + let mut key = [0; 32]; + key[31] = ctr; + key[30] = 1 + if inbound { 0 } else { 6 }; + a = SecretKey::from_slice(&key).unwrap(); + key[30] = 2 + if inbound { 0 } else { 6 }; + b = SecretKey::from_slice(&key).unwrap(); + key[30] = 3 + if inbound { 0 } else { 6 }; + c = SecretKey::from_slice(&key).unwrap(); + key[30] = 4 + if inbound { 0 } else { 6 }; + d = SecretKey::from_slice(&key).unwrap(); + key[30] = 5 + if inbound { 0 } else { 6 }; + e = SecretKey::from_slice(&key).unwrap(); + key[30] = 6 + if inbound { 0 } else { 6 }; + f = key; + let signer = InMemorySigner::new(&secp_ctx, a, b, c, d, e, f, keys_id, keys_id); + + TestChannelSigner::new_with_revoked(DynSigner::new(signer), state, false, false) } fn get_destination_script(&self, _channel_keys_id: [u8; 32]) -> Result { diff --git a/lightning-tests/Cargo.toml b/lightning-tests/Cargo.toml new file mode 100644 index 00000000000..75c68e03f5a --- /dev/null +++ b/lightning-tests/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "lightning-tests" +version = "0.0.1" +authors = ["Matt Corallo"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/lightningdevkit/rust-lightning/" +description = "Tests for LDK crates" +edition = "2021" + +[features] + +[dependencies] +lightning-types = { path = "../lightning-types", features = ["_test_utils"] } +lightning-invoice = { path = "../lightning-invoice", default-features = false } +lightning-macros = { path = "../lightning-macros" } +lightning = { path = "../lightning", features = ["_test_utils"] } +lightning_0_1 = { package = "lightning", version = "0.1.1", features = ["_test_utils"] } + +bitcoin = { version = "0.32.2", default-features = false } + +[dev-dependencies] diff --git a/lightning-tests/src/lib.rs b/lightning-tests/src/lib.rs new file mode 100644 index 00000000000..c028193d692 --- /dev/null +++ b/lightning-tests/src/lib.rs @@ -0,0 +1,5 @@ +#[cfg_attr(test, macro_use)] +extern crate lightning; + +#[cfg(all(test, not(taproot)))] +pub mod upgrade_downgrade_tests; diff --git a/lightning-tests/src/upgrade_downgrade_tests.rs b/lightning-tests/src/upgrade_downgrade_tests.rs new file mode 100644 index 00000000000..0a989553752 --- /dev/null +++ b/lightning-tests/src/upgrade_downgrade_tests.rs @@ -0,0 +1,65 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +//! Tests which test upgrading from previous versions of LDK or downgrading to previous versions of +//! LDK. + +use lightning_0_1::get_monitor as get_monitor_0_1; +use lightning_0_1::ln::functional_test_utils as lightning_0_1_utils; +use lightning_0_1::util::ser::Writeable; + +use lightning::ln::functional_test_utils::*; + +use lightning_types::payment::PaymentPreimage; + +#[test] +fn simple_upgrade() { + // Tests a simple case of upgrading from LDK 0.1 with a pending payment + let (node_a_ser, node_b_ser, mon_a_ser, mon_b_ser, preimage); + { + let chanmon_cfgs = lightning_0_1_utils::create_chanmon_cfgs(2); + let node_cfgs = lightning_0_1_utils::create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = lightning_0_1_utils::create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let nodes = lightning_0_1_utils::create_network(2, &node_cfgs, &node_chanmgrs); + + let chan_id = lightning_0_1_utils::create_announced_chan_between_nodes(&nodes, 0, 1).2; + + let payment_preimage = + lightning_0_1_utils::route_payment(&nodes[0], &[&nodes[1]], 1_000_000); + preimage = PaymentPreimage(payment_preimage.0 .0); + + node_a_ser = nodes[0].node.encode(); + node_b_ser = nodes[1].node.encode(); + mon_a_ser = get_monitor_0_1!(nodes[0], chan_id).encode(); + mon_b_ser = get_monitor_0_1!(nodes[1], chan_id).encode(); + } + + // Create a dummy node to reload over with the 0.1 state + + let mut chanmon_cfgs = create_chanmon_cfgs(2); + + // Our TestChannelSigner will fail as we're jumping ahead, so disable its state-based checks + chanmon_cfgs[0].keys_manager.disable_all_state_policy_checks = true; + chanmon_cfgs[1].keys_manager.disable_all_state_policy_checks = true; + + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let (persister_a, persister_b, chain_mon_a, chain_mon_b); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let (node_a, node_b); + let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + let config = test_default_channel_config(); + let a_mons = &[&mon_a_ser[..]]; + reload_node!(nodes[0], config.clone(), &node_a_ser, a_mons, persister_a, chain_mon_a, node_a); + reload_node!(nodes[1], config, &node_b_ser, &[&mon_b_ser], persister_b, chain_mon_b, node_b); + + reconnect_nodes(ReconnectArgs::new(&nodes[0], &nodes[1])); + + claim_payment(&nodes[0], &[&nodes[1]], preimage); +} diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 170d8261d5a..722b60e189d 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -13566,7 +13566,7 @@ where (13, htlc_onion_fields, optional_vec), (14, decode_update_add_htlcs_opt, option), (15, self.inbound_payment_id_secret, required), - (17, in_flight_monitor_updates, required), + (17, in_flight_monitor_updates, option), (19, peer_storage_dir, optional_vec), }); @@ -14097,7 +14097,7 @@ where (13, claimable_htlc_onion_fields, optional_vec), (14, decode_update_add_htlcs, option), (15, inbound_payment_id_secret, option), - (17, in_flight_monitor_updates, required), + (17, in_flight_monitor_updates, option), (19, peer_storage_dir, optional_vec), }); let mut decode_update_add_htlcs = decode_update_add_htlcs.unwrap_or_else(|| new_hash_map()); diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 8a9a5cb1762..2ebe489ce7a 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -1197,7 +1197,7 @@ pub fn _reload_node<'a, 'b, 'c>(node: &'a Node<'a, 'b, 'c>, default_config: User node_deserialized } -#[cfg(any(test, feature = "_externalize_tests"))] +#[macro_export] macro_rules! reload_node { ($node: expr, $new_config: expr, $chanman_encoded: expr, $monitors_encoded: expr, $persister: ident, $new_chain_monitor: ident, $new_channelmanager: ident) => { let chanman_encoded = $chanman_encoded; diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 6009a276976..fd98f78350e 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -1313,8 +1313,8 @@ where } } - #[cfg(any(test, feature = "_externalize_tests"))] - pub(crate) fn set_offers_handler(&mut self, offers_handler: OMH) { + #[cfg(any(test, feature = "_test_utils"))] + pub fn set_offers_handler(&mut self, offers_handler: OMH) { self.offers_handler = offers_handler; } diff --git a/lightning/src/util/test_channel_signer.rs b/lightning/src/util/test_channel_signer.rs index ea6451157cc..e619069af50 100644 --- a/lightning/src/util/test_channel_signer.rs +++ b/lightning/src/util/test_channel_signer.rs @@ -74,6 +74,7 @@ pub struct TestChannelSigner { /// Channel state used for policy enforcement pub state: Arc>, pub disable_revocation_policy_check: bool, + pub disable_all_state_policy_checks: bool, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -123,7 +124,12 @@ impl TestChannelSigner { /// Construct an TestChannelSigner pub fn new(inner: DynSigner) -> Self { let state = Arc::new(Mutex::new(EnforcementState::new())); - Self { inner, state, disable_revocation_policy_check: false } + Self { + inner, + state, + disable_revocation_policy_check: false, + disable_all_state_policy_checks: false, + } } /// Construct an TestChannelSigner with externally managed storage @@ -133,9 +139,9 @@ impl TestChannelSigner { /// here, usually by an implementation of KeysInterface. pub fn new_with_revoked( inner: DynSigner, state: Arc>, - disable_revocation_policy_check: bool, + disable_revocation_policy_check: bool, disable_all_state_policy_checks: bool, ) -> Self { - Self { inner, state, disable_revocation_policy_check } + Self { inner, state, disable_revocation_policy_check, disable_all_state_policy_checks } } #[cfg(any(test, feature = "_test_utils"))] @@ -175,12 +181,12 @@ impl ChannelSigner for TestChannelSigner { if !self.is_signer_available(SignerOp::ReleaseCommitmentSecret) { return Err(()); } - { - let mut state = self.state.lock().unwrap(); + let mut state = self.state.lock().unwrap(); + if !self.disable_all_state_policy_checks { assert!(idx == state.last_holder_revoked_commitment || idx == state.last_holder_revoked_commitment - 1, "can only revoke the current or next unrevoked commitment - trying {}, last revoked {}", idx, state.last_holder_revoked_commitment); assert!(idx > state.last_holder_commitment, "cannot revoke the last holder commitment - attempted to revoke {} last commitment {}", idx, state.last_holder_commitment); - state.last_holder_revoked_commitment = idx; } + state.last_holder_revoked_commitment = idx; self.inner.release_commitment_secret(idx) } @@ -190,12 +196,14 @@ impl ChannelSigner for TestChannelSigner { ) -> Result<(), ()> { let mut state = self.state.lock().unwrap(); let idx = holder_tx.commitment_number(); - assert!( - idx == state.last_holder_commitment || idx == state.last_holder_commitment - 1, - "expecting to validate the current or next holder commitment - trying {}, current {}", - idx, - state.last_holder_commitment - ); + if !self.disable_all_state_policy_checks { + assert!( + idx == state.last_holder_commitment || idx == state.last_holder_commitment - 1, + "expecting to validate the current or next holder commitment - trying {}, current {}", + idx, + state.last_holder_commitment + ); + } state.last_holder_commitment = idx; Ok(()) } @@ -206,7 +214,9 @@ impl ChannelSigner for TestChannelSigner { return Err(()); } let mut state = self.state.lock().unwrap(); - assert!(idx == state.last_counterparty_revoked_commitment || idx == state.last_counterparty_revoked_commitment - 1, "expecting to validate the current or next counterparty revocation - trying {}, current {}", idx, state.last_counterparty_revoked_commitment); + if !self.disable_all_state_policy_checks { + assert!(idx == state.last_counterparty_revoked_commitment || idx == state.last_counterparty_revoked_commitment - 1, "expecting to validate the current or next counterparty revocation - trying {}, current {}", idx, state.last_counterparty_revoked_commitment); + } state.last_counterparty_revoked_commitment = idx; Ok(()) } @@ -230,14 +240,14 @@ impl EcdsaChannelSigner for TestChannelSigner { ) -> Result<(Signature, Vec), ()> { self.verify_counterparty_commitment_tx(channel_parameters, commitment_tx, secp_ctx); - { - #[cfg(test)] - if !self.is_signer_available(SignerOp::SignCounterpartyCommitment) { - return Err(()); - } - let mut state = self.state.lock().unwrap(); - let actual_commitment_number = commitment_tx.commitment_number(); - let last_commitment_number = state.last_counterparty_commitment; + #[cfg(test)] + if !self.is_signer_available(SignerOp::SignCounterpartyCommitment) { + return Err(()); + } + let mut state = self.state.lock().unwrap(); + let actual_commitment_number = commitment_tx.commitment_number(); + let last_commitment_number = state.last_counterparty_commitment; + if !self.disable_all_state_policy_checks { // These commitment numbers are backwards counting. We expect either the same as the previously encountered, // or the next one. assert!( @@ -255,9 +265,9 @@ impl EcdsaChannelSigner for TestChannelSigner { actual_commitment_number, state.last_counterparty_revoked_commitment ); - state.last_counterparty_commitment = - cmp::min(last_commitment_number, actual_commitment_number) } + state.last_counterparty_commitment = + cmp::min(last_commitment_number, actual_commitment_number); Ok(self .inner @@ -281,14 +291,16 @@ impl EcdsaChannelSigner for TestChannelSigner { } let trusted_tx = self.verify_holder_commitment_tx(channel_parameters, commitment_tx, secp_ctx); - let state = self.state.lock().unwrap(); - let commitment_number = trusted_tx.commitment_number(); - if state.last_holder_revoked_commitment - 1 != commitment_number - && state.last_holder_revoked_commitment - 2 != commitment_number - { - if !self.disable_revocation_policy_check { - panic!("can only sign the next two unrevoked commitment numbers, revoked={} vs requested={} for {}", - state.last_holder_revoked_commitment, commitment_number, self.inner.channel_keys_id()[0]) + if !self.disable_all_state_policy_checks { + let state = self.state.lock().unwrap(); + let commitment_number = trusted_tx.commitment_number(); + if state.last_holder_revoked_commitment - 1 != commitment_number + && state.last_holder_revoked_commitment - 2 != commitment_number + { + if !self.disable_revocation_policy_check { + panic!("can only sign the next two unrevoked commitment numbers, revoked={} vs requested={} for {}", + state.last_holder_revoked_commitment, commitment_number, self.inner.channel_keys_id()[0]) + } } } Ok(self.inner.sign_holder_commitment(channel_parameters, commitment_tx, secp_ctx).unwrap()) @@ -356,13 +368,15 @@ impl EcdsaChannelSigner for TestChannelSigner { if !self.is_signer_available(SignerOp::SignHolderHtlcTransaction) { return Err(()); } - let state = self.state.lock().unwrap(); - if state.last_holder_revoked_commitment - 1 != htlc_descriptor.per_commitment_number - && state.last_holder_revoked_commitment - 2 != htlc_descriptor.per_commitment_number - { - if !self.disable_revocation_policy_check { - panic!("can only sign the next two unrevoked commitment numbers, revoked={} vs requested={} for {}", - state.last_holder_revoked_commitment, htlc_descriptor.per_commitment_number, self.inner.channel_keys_id()[0]) + if !self.disable_all_state_policy_checks { + let state = self.state.lock().unwrap(); + if state.last_holder_revoked_commitment - 1 != htlc_descriptor.per_commitment_number + && state.last_holder_revoked_commitment - 2 != htlc_descriptor.per_commitment_number + { + if !self.disable_revocation_policy_check { + panic!("can only sign the next two unrevoked commitment numbers, revoked={} vs requested={} for {}", + state.last_holder_revoked_commitment, htlc_descriptor.per_commitment_number, self.inner.channel_keys_id()[0]) + } } } assert_eq!(htlc_tx.input[input], htlc_descriptor.unsigned_tx_input()); diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index f90bfb97ef7..00b85cc1ef8 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -1531,6 +1531,7 @@ pub struct TestKeysInterface { pub backing: DynKeysInterface, pub override_random_bytes: Mutex>, pub disable_revocation_policy_check: bool, + pub disable_all_state_policy_checks: bool, enforcement_states: Mutex>>>, expectations: Mutex>>, pub unavailable_signers_ops: Mutex>>, @@ -1599,8 +1600,9 @@ impl SignerProvider for TestKeysInterface { fn derive_channel_signer(&self, channel_keys_id: [u8; 32]) -> TestChannelSigner { let keys = self.backing.derive_channel_signer(channel_keys_id); let state = self.make_enforcement_state_cell(keys.channel_keys_id()); - let signer = - TestChannelSigner::new_with_revoked(keys, state, self.disable_revocation_policy_check); + let rev_checks = self.disable_revocation_policy_check; + let state_checks = self.disable_all_state_policy_checks; + let signer = TestChannelSigner::new_with_revoked(keys, state, rev_checks, state_checks); #[cfg(test)] if let Some(ops) = self.unavailable_signers_ops.lock().unwrap().get(&channel_keys_id) { for &op in ops { @@ -1668,6 +1670,7 @@ impl TestKeysInterface { backing: DynKeysInterface::new(backing), override_random_bytes: Mutex::new(None), disable_revocation_policy_check: false, + disable_all_state_policy_checks: false, enforcement_states: Mutex::new(new_hash_map()), expectations: Mutex::new(None), unavailable_signers_ops: Mutex::new(new_hash_map()),