Skip to content

Commit e41e756

Browse files
committed
Expose functional tests under _externalize_tests feature flag
Also, introduce TestSignerFactory, a factory for dynamic signers and ext-functional-test-demo crate for testing this machinery.
1 parent 17cd6e3 commit e41e756

32 files changed

+559
-490
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ lightning-rapid-gossip-sync/res/full_graph.lngossip
1313
lightning-custom-message/target
1414
lightning-transaction-sync/target
1515
lightning-dns-resolver/target
16+
ext-functional-test-demo/target
1617
no-std-check/target
1718
msrv-no-dev-deps-check/target

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ members = [
2121

2222
exclude = [
2323
"lightning-transaction-sync",
24+
"ext-functional-test-demo",
2425
"no-std-check",
2526
"msrv-no-dev-deps-check",
2627
"bench",

ci/ci-tests.sh

+7
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ cargo check --verbose --color always
112112
[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
113113
popd
114114

115+
echo -e "\n\Running functional tests from outside the workspace"
116+
pushd ext-functional-test-demo
117+
[ "$RUSTC_MINOR_VERSION" -lt 65 ] && cargo update -p regex --precise "1.9.6" --verbose
118+
cargo test --color always
119+
[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
120+
popd
121+
115122
# Test that we can build downstream code with only the "release pins".
116123
pushd msrv-no-dev-deps-check
117124
PIN_RELEASE_DEPS

ext-functional-test-demo/Cargo.toml

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "ext-functional-tester"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
lightning = { path = "../lightning", features = ["_externalize_tests"] }

ext-functional-test-demo/src/main.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
fn main() {
2+
println!("{} tests were exported", lightning::get_xtests().len());
3+
}
4+
5+
#[cfg(test)]
6+
mod tests {
7+
use lightning::util::dyn_signer::{DynKeysInterfaceTrait, DynSigner};
8+
use lightning::util::test_utils::{TestSignerFactory, SIGNER_FACTORY};
9+
use std::panic::catch_unwind;
10+
use std::sync::Arc;
11+
use std::time::Duration;
12+
13+
struct BrokenSignerFactory();
14+
15+
impl TestSignerFactory for BrokenSignerFactory {
16+
fn make_signer(
17+
&self, _seed: &[u8; 32], _now: Duration,
18+
) -> Box<dyn DynKeysInterfaceTrait<EcdsaSigner = DynSigner>> {
19+
panic!()
20+
}
21+
}
22+
23+
#[test]
24+
fn test_functional() {
25+
lightning::ln::functional_tests::test_insane_channel_opens();
26+
lightning::ln::functional_tests::fake_network_test();
27+
28+
SIGNER_FACTORY.set(Arc::new(BrokenSignerFactory()));
29+
catch_unwind(|| lightning::ln::functional_tests::fake_network_test()).unwrap_err();
30+
}
31+
}

fuzz/src/chanmon_consistency.rs

+2
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ use bitcoin::secp256k1::schnorr;
7979
use bitcoin::secp256k1::{self, Message, PublicKey, Scalar, Secp256k1, SecretKey};
8080

8181
use lightning::io::Cursor;
82+
use lightning::util::dyn_signer::DynSigner;
8283
use std::cmp::{self, Ordering};
8384
use std::mem;
8485
use std::sync::atomic;
@@ -375,6 +376,7 @@ impl SignerProvider for KeyProvider {
375376
channel_keys_id,
376377
);
377378
let revoked_commitment = self.make_enforcement_state_cell(keys.commitment_seed);
379+
let keys = DynSigner::new(keys);
378380
TestChannelSigner::new_with_revoked(keys, revoked_commitment, false)
379381
}
380382

fuzz/src/full_stack.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
7575
use bitcoin::secp256k1::schnorr;
7676
use bitcoin::secp256k1::{self, Message, PublicKey, Scalar, Secp256k1, SecretKey};
7777

78+
use lightning::util::dyn_signer::DynSigner;
7879
use std::cell::RefCell;
7980
use std::cmp;
8081
use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
@@ -439,7 +440,7 @@ impl SignerProvider for KeyProvider {
439440
let ctr = channel_keys_id[0];
440441
let (inbound, state) = self.signer_state.borrow().get(&ctr).unwrap().clone();
441442
TestChannelSigner::new_with_revoked(
442-
if inbound {
443+
DynSigner::new(if inbound {
443444
InMemorySigner::new(
444445
&secp_ctx,
445446
SecretKey::from_slice(&[
@@ -509,7 +510,7 @@ impl SignerProvider for KeyProvider {
509510
channel_keys_id,
510511
channel_keys_id,
511512
)
512-
},
513+
}),
513514
state,
514515
false,
515516
)

lightning-background-processor/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -2550,6 +2550,8 @@ mod tests {
25502550
failure: PathFailure::OnPath { network_update: None },
25512551
path: path.clone(),
25522552
short_channel_id: Some(scored_scid),
2553+
error_code: None,
2554+
error_data: None,
25532555
});
25542556
let event = $receive.expect("PaymentPathFailed not handled within deadline");
25552557
match event {
@@ -2567,6 +2569,8 @@ mod tests {
25672569
failure: PathFailure::OnPath { network_update: None },
25682570
path: path.clone(),
25692571
short_channel_id: None,
2572+
error_code: None,
2573+
error_data: None,
25702574
});
25712575
let event = $receive.expect("PaymentPathFailed not handled within deadline");
25722576
match event {

lightning-macros/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ pub fn drop_legacy_field_definition(expr: TokenStream) -> TokenStream {
306306
///
307307
/// fn f1() {}
308308
///
309-
/// #[xtest(feature = "_test_utils")]
309+
/// #[xtest(feature = "_externalize_tests")]
310310
/// pub fn test_f1() {
311311
/// f1();
312312
/// }

lightning/Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ rustdoc-args = ["--cfg", "docsrs"]
1818
[features]
1919
# Internal test utilities exposed to other repo crates
2020
_test_utils = ["regex", "bitcoin/bitcoinconsensus", "lightning-types/_test_utils"]
21-
21+
_externalize_tests = ["inventory", "_test_utils"]
2222
# Allow signing of local transactions that may have been revoked or will be revoked, for functional testing (e.g. justice tx handling).
2323
# This is unsafe to use in production because it may result in the counterparty publishing taking our funds.
2424
unsafe_revoked_tx_signing = []
@@ -48,10 +48,12 @@ regex = { version = "1.5.6", optional = true }
4848
backtrace = { version = "0.3", optional = true }
4949

5050
libm = { version = "0.2", default-features = false }
51+
inventory = { version = "0.3", optional = true }
5152

5253
[dev-dependencies]
5354
regex = "1.5.6"
5455
lightning-types = { version = "0.3.0", path = "../lightning-types", features = ["_test_utils"] }
56+
lightning-macros = { path = "../lightning-macros" }
5557

5658
[dev-dependencies.bitcoin]
5759
version = "0.32.2"

lightning/src/chain/chainmonitor.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ where C::Target: chain::Filter,
469469
}
470470

471471

472-
#[cfg(test)]
472+
#[cfg(any(test, feature = "_test_utils"))]
473473
pub fn remove_monitor(&self, channel_id: &ChannelId) -> ChannelMonitor<ChannelSigner> {
474474
self.monitors.write().unwrap().remove(channel_id).unwrap().monitor
475475
}

lightning/src/chain/channelmonitor.rs

+8-11
Original file line numberDiff line numberDiff line change
@@ -854,10 +854,7 @@ impl Readable for IrrevocablyResolvedHTLC {
854854
/// returned block hash and the the current chain and then reconnecting blocks to get to the
855855
/// best chain) upon deserializing the object!
856856
pub struct ChannelMonitor<Signer: EcdsaChannelSigner> {
857-
#[cfg(test)]
858857
pub(crate) inner: Mutex<ChannelMonitorImpl<Signer>>,
859-
#[cfg(not(test))]
860-
pub(super) inner: Mutex<ChannelMonitorImpl<Signer>>,
861858
}
862859

863860
impl<Signer: EcdsaChannelSigner> Clone for ChannelMonitor<Signer> where Signer: Clone {
@@ -965,9 +962,9 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
965962
// Obviously Correct (tm) if we just keep track of them explicitly.
966963
outputs_to_watch: HashMap<Txid, Vec<(u32, ScriptBuf)>>,
967964

968-
#[cfg(test)]
965+
#[cfg(any(test, feature = "_test_utils"))]
969966
pub onchain_tx_handler: OnchainTxHandler<Signer>,
970-
#[cfg(not(test))]
967+
#[cfg(not(any(test, feature = "_test_utils")))]
971968
onchain_tx_handler: OnchainTxHandler<Signer>,
972969

973970
// This is set when the Channel[Manager] generated a ChannelMonitorUpdate which indicated the
@@ -1818,7 +1815,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
18181815
/// Unsafe test-only version of `broadcast_latest_holder_commitment_txn` used by our test framework
18191816
/// to bypass HolderCommitmentTransaction state update lockdown after signature and generate
18201817
/// revoked commitment transaction.
1821-
#[cfg(any(test, feature = "unsafe_revoked_tx_signing"))]
1818+
#[cfg(any(test, feature = "_test_utils", feature = "unsafe_revoked_tx_signing"))]
18221819
pub fn unsafe_get_latest_holder_commitment_txn<L: Deref>(&self, logger: &L) -> Vec<Transaction>
18231820
where L::Target: Logger {
18241821
let mut inner = self.inner.lock().unwrap();
@@ -2132,7 +2129,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
21322129
self.inner.lock().unwrap().counterparty_payment_script = script;
21332130
}
21342131

2135-
#[cfg(test)]
2132+
#[cfg(any(test, feature = "_test_utils"))]
21362133
pub fn do_mut_signer_call<F: FnMut(&mut Signer) -> ()>(&self, mut f: F) {
21372134
let mut inner = self.inner.lock().unwrap();
21382135
f(&mut inner.onchain_tx_handler.signer);
@@ -2773,7 +2770,7 @@ macro_rules! fail_unbroadcast_htlcs {
27732770
// witness length match (ie is 136 bytes long). We generate one here which we also use in some
27742771
// in-line tests later.
27752772

2776-
#[cfg(test)]
2773+
#[cfg(any(test, feature = "_test_utils"))]
27772774
pub fn deliberately_bogus_accepted_htlc_witness_program() -> Vec<u8> {
27782775
use bitcoin::opcodes;
27792776
let mut ret = [opcodes::all::OP_NOP.to_u8(); 136];
@@ -2785,7 +2782,7 @@ pub fn deliberately_bogus_accepted_htlc_witness_program() -> Vec<u8> {
27852782
Vec::from(&ret[..])
27862783
}
27872784

2788-
#[cfg(test)]
2785+
#[cfg(any(test, feature = "_test_utils"))]
27892786
pub fn deliberately_bogus_accepted_htlc_witness() -> Vec<Vec<u8>> {
27902787
vec![Vec::new(), Vec::new(), Vec::new(), Vec::new(), deliberately_bogus_accepted_htlc_witness_program().into()].into()
27912788
}
@@ -3947,7 +3944,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
39473944
}
39483945
}
39493946

3950-
#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
3947+
#[cfg(any(test, feature = "_test_utils", feature = "unsafe_revoked_tx_signing"))]
39513948
/// Note that this includes possibly-locktimed-in-the-future transactions!
39523949
fn unsafe_get_latest_holder_commitment_txn<L: Deref>(
39533950
&mut self, logger: &WithChannelMonitor<L>
@@ -5289,7 +5286,7 @@ mod tests {
52895286
nodes[1].chain_monitor.chain_monitor.transactions_confirmed(&new_header,
52905287
&[(0, broadcast_tx)], conf_height);
52915288

5292-
let (_, pre_update_monitor) = <(BlockHash, ChannelMonitor<InMemorySigner>)>::read(
5289+
let (_, pre_update_monitor) = <(BlockHash, ChannelMonitor<_>)>::read(
52935290
&mut io::Cursor::new(&get_monitor!(nodes[1], channel.2).encode()),
52945291
(&nodes[1].keys_manager.backing, &nodes[1].keys_manager.backing)).unwrap();
52955292

lightning/src/chain/onchaintx.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,9 @@ pub struct OnchainTxHandler<ChannelSigner: EcdsaChannelSigner> {
250250
// Key is identifier of the pending claim request, i.e the txid of the initial claiming transaction generated by
251251
// us and is immutable until all outpoint of the claimable set are post-anti-reorg-delay solved.
252252
// Entry is cache of elements need to generate a bumped claiming transaction (see ClaimTxBumpMaterial)
253-
#[cfg(test)] // Used in functional_test to verify sanitization
253+
#[cfg(any(test, feature = "_test_utils"))]
254254
pub(crate) pending_claim_requests: HashMap<ClaimId, PackageTemplate>,
255-
#[cfg(not(test))]
255+
#[cfg(not(any(test, feature = "_test_utils")))]
256256
pending_claim_requests: HashMap<ClaimId, PackageTemplate>,
257257

258258
// Used to track external events that need to be forwarded to the `ChainMonitor`. This `Vec`
@@ -273,9 +273,9 @@ pub struct OnchainTxHandler<ChannelSigner: EcdsaChannelSigner> {
273273
// block height, and are immutable until the outpoint has enough confirmations to meet our
274274
// [`ANTI_REORG_DELAY`]. The initial confirmation block height is used to remove the entry if
275275
// the block gets disconnected.
276-
#[cfg(test)] // Used in functional_test to verify sanitization
277-
pub claimable_outpoints: HashMap<BitcoinOutPoint, (ClaimId, u32)>,
278-
#[cfg(not(test))]
276+
#[cfg(any(test, feature = "_test_utils"))]
277+
pub(crate) claimable_outpoints: HashMap<BitcoinOutPoint, (ClaimId, u32)>,
278+
#[cfg(not(any(test, feature = "_test_utils")))]
279279
claimable_outpoints: HashMap<BitcoinOutPoint, (ClaimId, u32)>,
280280

281281
locktimed_packages: BTreeMap<u32, Vec<PackageTemplate>>,
@@ -1193,7 +1193,7 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
11931193
MaybeSignedTransaction(tx)
11941194
}
11951195

1196-
#[cfg(any(test, feature="unsafe_revoked_tx_signing"))]
1196+
#[cfg(any(test, feature="_test_utils", feature="unsafe_revoked_tx_signing"))]
11971197
pub(crate) fn get_fully_signed_copy_holder_tx(&mut self, funding_redeemscript: &Script) -> Transaction {
11981198
let sig = self.signer.unsafe_sign_holder_commitment(&self.channel_transaction_parameters, &self.holder_commitment, &self.secp_ctx).expect("sign holder commitment");
11991199
self.holder_commitment.add_holder_sig(funding_redeemscript, sig)

lightning/src/chain/package.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,10 @@ pub(crate) fn verify_channel_type_features(channel_type_features: &Option<Channe
105105
// number_of_witness_elements + sig_length + revocation_sig + true_length + op_true + witness_script_length + witness_script
106106
pub(crate) const WEIGHT_REVOKED_OUTPUT: u64 = 1 + 1 + 73 + 1 + 1 + 1 + 77;
107107

108-
#[cfg(not(test))]
108+
#[cfg(not(any(test, feature = "_test_utils")))]
109109
/// Height delay at which transactions are fee-bumped/rebroadcasted with a low priority.
110110
const LOW_FREQUENCY_BUMP_INTERVAL: u32 = 15;
111-
#[cfg(test)]
111+
#[cfg(any(test, feature = "_test_utils"))]
112112
/// Height delay at which transactions are fee-bumped/rebroadcasted with a low priority.
113113
pub(crate) const LOW_FREQUENCY_BUMP_INTERVAL: u32 = 15;
114114

lightning/src/events/mod.rs

+11-11
Original file line numberDiff line numberDiff line change
@@ -1054,9 +1054,9 @@ pub enum Event {
10541054
/// If this is `Some`, then the corresponding channel should be avoided when the payment is
10551055
/// retried. May be `None` for older [`Event`] serializations.
10561056
short_channel_id: Option<u64>,
1057-
#[cfg(test)]
1057+
#[cfg(any(test, feature = "_test_utils"))]
10581058
error_code: Option<u16>,
1059-
#[cfg(test)]
1059+
#[cfg(any(test, feature = "_test_utils"))]
10601060
error_data: Option<Vec<u8>>,
10611061
},
10621062
/// Indicates that a probe payment we sent returned successful, i.e., only failed at the destination.
@@ -1569,15 +1569,15 @@ impl Writeable for Event {
15691569
&Event::PaymentPathFailed {
15701570
ref payment_id, ref payment_hash, ref payment_failed_permanently, ref failure,
15711571
ref path, ref short_channel_id,
1572-
#[cfg(test)]
1572+
#[cfg(any(test, feature = "_test_utils"))]
15731573
ref error_code,
1574-
#[cfg(test)]
1574+
#[cfg(any(test, feature = "_test_utils"))]
15751575
ref error_data,
15761576
} => {
15771577
3u8.write(writer)?;
1578-
#[cfg(test)]
1578+
#[cfg(any(test, feature = "_test_utils"))]
15791579
error_code.write(writer)?;
1580-
#[cfg(test)]
1580+
#[cfg(any(test, feature = "_test_utils"))]
15811581
error_data.write(writer)?;
15821582
write_tlv_fields!(writer, {
15831583
(0, payment_hash, required),
@@ -1920,9 +1920,9 @@ impl MaybeReadable for Event {
19201920
},
19211921
3u8 => {
19221922
let mut f = || {
1923-
#[cfg(test)]
1923+
#[cfg(any(test, feature = "_test_utils"))]
19241924
let error_code = Readable::read(reader)?;
1925-
#[cfg(test)]
1925+
#[cfg(any(test, feature = "_test_utils"))]
19261926
let error_data = Readable::read(reader)?;
19271927
let mut payment_hash = PaymentHash([0; 32]);
19281928
let mut payment_failed_permanently = false;
@@ -1952,9 +1952,9 @@ impl MaybeReadable for Event {
19521952
failure,
19531953
path: Path { hops: path.unwrap(), blinded_tail },
19541954
short_channel_id,
1955-
#[cfg(test)]
1955+
#[cfg(any(test, feature = "_test_utils"))]
19561956
error_code,
1957-
#[cfg(test)]
1957+
#[cfg(any(test, feature = "_test_utils"))]
19581958
error_data,
19591959
}))
19601960
};
@@ -2364,7 +2364,7 @@ impl MaybeReadable for Event {
23642364
/// broadcast to most peers).
23652365
/// These events are handled by PeerManager::process_events if you are using a PeerManager.
23662366
#[derive(Clone, Debug)]
2367-
#[cfg_attr(test, derive(PartialEq))]
2367+
#[cfg_attr(any(test, feature = "_test_utils"), derive(PartialEq))]
23682368
pub enum MessageSendEvent {
23692369
/// Used to indicate that we've accepted a channel open and should send the accept_channel
23702370
/// message provided to the given peer.

lightning/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,6 @@ mod prelude {
142142
extern crate backtrace;
143143

144144
mod sync;
145+
146+
#[cfg(feature = "_externalize_tests")]
147+
lightning_macros::xtest_inventory!();

lightning/src/ln/bolt11_payment.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ fn params_from_invoice(
8888
mod tests {
8989
use super::*;
9090
use crate::routing::router::Payee;
91+
use crate::sign::{NodeSigner, Recipient};
9192
use crate::types::payment::PaymentSecret;
9293
use bitcoin::hashes::sha256::Hash as Sha256;
9394
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
@@ -178,8 +179,6 @@ mod tests {
178179
let (payment_hash, payment_secret) =
179180
nodes[1].node.create_inbound_payment(None, 7200, None).unwrap();
180181

181-
let secp_ctx = Secp256k1::new();
182-
let node_secret = nodes[1].keys_manager.backing.get_node_secret_key();
183182
let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
184183
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
185184
.description("test".into())
@@ -189,8 +188,11 @@ mod tests {
189188
.min_final_cltv_expiry_delta(144)
190189
.amount_milli_satoshis(50_000)
191190
.payment_metadata(payment_metadata.clone())
192-
.build_signed(|hash| secp_ctx.sign_ecdsa_recoverable(hash, &node_secret))
191+
.build_raw()
193192
.unwrap();
193+
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
194+
let invoice = invoice.sign::<_, ()>(|_| Ok(sig)).unwrap();
195+
let invoice = Bolt11Invoice::from_signed(invoice).unwrap();
194196

195197
let (hash, onion, params) = payment_parameters_from_invoice(&invoice).unwrap();
196198
nodes[0]

0 commit comments

Comments
 (0)