Skip to content

Commit 971aee2

Browse files
review: fix Kotlin codegen collision, add tests, improve docs
- Rename FFI PaidBolt12Invoice variants from Bolt12Invoice/StaticInvoice to Bolt12/Static to avoid Kotlin sealed class name collisions where inner data classes shadowed top-level types of the same name. - Add serialization round-trip test in bolt12_proof_of_payment to validate TLV tag 7 backward compatibility. - Assert bolt12_invoice is None for BOLT11 payments in do_channel_full_cycle, covering all tests that use this helper. - Add explanatory comments on the cfg-gated PaidBolt12Invoice re-export pattern and the clone-based Writeable impl. - Apply cargo fmt to pre-existing formatting issues in lib.rs and lnurl_auth.rs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 991fa56 commit 971aee2

File tree

6 files changed

+60
-34
lines changed

6 files changed

+60
-34
lines changed

src/ffi/types.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
// Make sure to add any re-exported items that need to be used in uniffi below.
1212

13+
use std::collections::HashMap;
1314
use std::convert::TryInto;
1415
use std::ops::Deref;
1516
use std::str::FromStr;
@@ -44,10 +45,10 @@ pub use lightning_liquidity::lsps1::msgs::{
4445
};
4546
pub use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
4647
pub use lightning_types::string::UntrustedString;
47-
use std::collections::HashMap;
48-
49-
use vss_client::headers::VssHeaderProvider as VssClientHeaderProvider;
50-
use vss_client::headers::VssHeaderProviderError as VssClientHeaderProviderError;
48+
use vss_client::headers::{
49+
VssHeaderProvider as VssClientHeaderProvider,
50+
VssHeaderProviderError as VssClientHeaderProviderError,
51+
};
5152

5253
/// Errors around providing headers for each VSS request.
5354
#[derive(Debug, uniffi::Error)]
@@ -817,23 +818,27 @@ impl AsRef<LdkStaticInvoice> for StaticInvoice {
817818
/// The BOLT12 invoice that was paid, surfaced in [`Event::PaymentSuccessful`].
818819
///
819820
/// [`Event::PaymentSuccessful`]: crate::Event::PaymentSuccessful
821+
///
822+
/// Note: The variant names intentionally differ from their contained types to avoid
823+
/// name collisions in Kotlin codegen, where sealed class inner classes would shadow
824+
/// top-level types of the same name.
820825
#[derive(Debug, Clone, PartialEq, Eq, uniffi::Enum)]
821826
pub enum PaidBolt12Invoice {
822827
/// The BOLT12 invoice, allowing the user to perform proof of payment.
823-
Bolt12Invoice(Arc<Bolt12Invoice>),
828+
Bolt12(Arc<Bolt12Invoice>),
824829
/// The static invoice, used in async payments, where the user cannot perform proof of
825830
/// payment.
826-
StaticInvoice(Arc<StaticInvoice>),
831+
Static(Arc<StaticInvoice>),
827832
}
828833

829834
impl From<LdkPaidBolt12Invoice> for PaidBolt12Invoice {
830835
fn from(ldk: LdkPaidBolt12Invoice) -> Self {
831836
match ldk {
832837
LdkPaidBolt12Invoice::Bolt12Invoice(invoice) => {
833-
PaidBolt12Invoice::Bolt12Invoice(Arc::new(Bolt12Invoice::from(invoice)))
838+
PaidBolt12Invoice::Bolt12(Arc::new(Bolt12Invoice::from(invoice)))
834839
},
835840
LdkPaidBolt12Invoice::StaticInvoice(invoice) => {
836-
PaidBolt12Invoice::StaticInvoice(Arc::new(StaticInvoice::from(invoice)))
841+
PaidBolt12Invoice::Static(Arc::new(StaticInvoice::from(invoice)))
837842
},
838843
}
839844
}
@@ -842,10 +847,10 @@ impl From<LdkPaidBolt12Invoice> for PaidBolt12Invoice {
842847
impl From<PaidBolt12Invoice> for LdkPaidBolt12Invoice {
843848
fn from(wrapper: PaidBolt12Invoice) -> Self {
844849
match wrapper {
845-
PaidBolt12Invoice::Bolt12Invoice(invoice) => {
850+
PaidBolt12Invoice::Bolt12(invoice) => {
846851
LdkPaidBolt12Invoice::Bolt12Invoice(invoice.inner.clone())
847852
},
848-
PaidBolt12Invoice::StaticInvoice(invoice) => {
853+
PaidBolt12Invoice::Static(invoice) => {
849854
LdkPaidBolt12Invoice::StaticInvoice(invoice.inner.clone())
850855
},
851856
}
@@ -854,6 +859,8 @@ impl From<PaidBolt12Invoice> for LdkPaidBolt12Invoice {
854859

855860
impl Writeable for PaidBolt12Invoice {
856861
fn write<W: Writer>(&self, w: &mut W) -> Result<(), lightning::io::Error> {
862+
// We clone `self` to convert it into the LDK native type for serialization.
863+
// This only runs during event persistence, so the overhead is acceptable.
857864
let ldk_type: LdkPaidBolt12Invoice = self.clone().into();
858865
ldk_type.write(w)
859866
}

src/lib.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,6 @@ use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
115115
use std::{any::Any, sync::Weak};
116116

117117
pub use balance::{BalanceDetails, LightningBalance, PendingSweepBalance};
118-
pub use bip39;
119-
pub use bitcoin;
120118
use bitcoin::secp256k1::PublicKey;
121119
use bitcoin::{Address, Amount};
122120
#[cfg(feature = "uniffi")]
@@ -141,7 +139,6 @@ use ffi::*;
141139
use gossip::GossipSource;
142140
use graph::NetworkGraph;
143141
use io::utils::write_node_metrics;
144-
pub use lightning;
145142
use lightning::chain::BestBlock;
146143
use lightning::impl_writeable_tlv_based;
147144
use lightning::ln::chan_utils::FUNDING_TRANSACTION_WITNESS_WEIGHT;
@@ -153,9 +150,6 @@ use lightning::sign::EntropySource;
153150
use lightning::util::persist::KVStoreSync;
154151
use lightning::util::wallet_utils::{Input, Wallet as LdkWallet};
155152
use lightning_background_processor::process_events_async;
156-
pub use lightning_invoice;
157-
pub use lightning_liquidity;
158-
pub use lightning_types;
159153
use liquidity::{LSPS1Liquidity, LiquiditySource};
160154
use lnurl_auth::LnurlAuth;
161155
use logger::{log_debug, log_error, log_info, log_trace, LdkLogger, Logger};
@@ -167,14 +161,16 @@ use payment::{
167161
};
168162
use peer_store::{PeerInfo, PeerStore};
169163
use runtime::Runtime;
170-
pub use tokio;
171164
use types::{
172165
Broadcaster, BumpTransactionEventHandler, ChainMonitor, ChannelManager, DynStore, Graph,
173166
HRNResolver, KeysManager, OnionMessenger, PaymentStore, PeerManager, Router, Scorer, Sweeper,
174167
Wallet,
175168
};
176169
pub use types::{ChannelDetails, CustomTlvRecord, PeerDetails, SyncAndAsyncKVStore, UserChannelId};
177-
pub use vss_client;
170+
pub use {
171+
bip39, bitcoin, lightning, lightning_invoice, lightning_liquidity, lightning_types, tokio,
172+
vss_client,
173+
};
178174

179175
use crate::scoring::setup_background_pathfinding_scores_sync;
180176
use crate::wallet::FundingAmount;

src/lnurl_auth.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,19 @@
55
// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
66
// accordance with one or both of these licenses.
77

8-
use crate::logger::{log_debug, log_error, Logger};
9-
use crate::Error;
8+
use std::sync::Arc;
109

10+
use bitcoin::bech32;
1111
use bitcoin::bip32::{ChildNumber, Xpriv};
12-
use bitcoin::hashes::{hex::FromHex, sha256, Hash, HashEngine, Hmac, HmacEngine};
12+
use bitcoin::hashes::hex::FromHex;
13+
use bitcoin::hashes::{sha256, Hash, HashEngine, Hmac, HmacEngine};
1314
use bitcoin::secp256k1::{All, Message, Secp256k1, SecretKey};
14-
use lightning::util::logger::Logger as LdkLogger;
15-
16-
use bitcoin::bech32;
1715
use bitreq::Client;
16+
use lightning::util::logger::Logger as LdkLogger;
1817
use serde::{Deserialize, Serialize};
19-
use std::sync::Arc;
18+
19+
use crate::logger::{log_debug, log_error, Logger};
20+
use crate::Error;
2021

2122
/// The BIP-32 hardened child index used for LNURL-auth key derivation as defined by LUD-05.
2223
pub(crate) const LNURL_AUTH_HARDENED_CHILD_INDEX: u32 = 138;
@@ -188,9 +189,10 @@ fn linking_key_path(hashing_key: &[u8; 32], domain_name: &str) -> Vec<ChildNumbe
188189

189190
#[cfg(test)]
190191
mod tests {
191-
use super::*;
192192
use bitcoin::Network;
193193

194+
use super::*;
195+
194196
fn build_auth(seed: [u8; 32]) -> LnurlAuth {
195197
let logger = Arc::new(Logger::new_log_facade());
196198
let xprv = Xpriv::new_master(Network::Bitcoin, &seed).unwrap();

src/payment/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ pub use store::{
2626
};
2727
pub use unified::{UnifiedPayment, UnifiedPaymentResult};
2828

29+
// We re-export `PaidBolt12Invoice` from different modules depending on the `uniffi` feature.
30+
// Under UniFFI, enum variants containing non-primitive types must be `Arc`-wrapped, so we use a
31+
// custom wrapper defined in `ffi::types`. Without UniFFI, we re-export the LDK native type directly.
2932
#[cfg(feature = "uniffi")]
3033
pub use crate::ffi::PaidBolt12Invoice;
3134
#[cfg(not(feature = "uniffi"))]

tests/common/mod.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,17 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
953953
});
954954
assert_eq!(inbound_payments_b.len(), 1);
955955

956-
expect_event!(node_a, PaymentSuccessful);
956+
// Verify bolt12_invoice is None for BOLT11 payments
957+
match node_a.next_event_async().await {
958+
ref e @ Event::PaymentSuccessful { ref bolt12_invoice, .. } => {
959+
println!("{} got event {:?}", node_a.node_id(), e);
960+
assert!(bolt12_invoice.is_none(), "bolt12_invoice should be None for BOLT11 payments");
961+
node_a.event_handled().unwrap();
962+
},
963+
ref e => {
964+
panic!("{} got unexpected event!: {:?}", std::stringify!(node_a), e);
965+
},
966+
}
957967
expect_event!(node_b, PaymentReceived);
958968
assert_eq!(node_a.payment(&payment_id).unwrap().status, PaymentStatus::Succeeded);
959969
assert_eq!(node_a.payment(&payment_id).unwrap().direction, PaymentDirection::Outbound);

tests/integration_tests_rust.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use ldk_node::{Builder, Event, NodeError};
3737
use lightning::ln::channelmanager::PaymentId;
3838
use lightning::routing::gossip::{NodeAlias, NodeId};
3939
use lightning::routing::router::RouteParametersConfig;
40+
use lightning::util::ser::{Readable, Writeable};
4041
use lightning_invoice::{Bolt11InvoiceDescription, Description};
4142
use lightning_types::payment::{PaymentHash, PaymentPreimage};
4243
use log::LevelFilter;
@@ -1317,37 +1318,44 @@ async fn bolt12_proof_of_payment() {
13171318
node_a.bolt12_payment().send(&offer, Some(1), Some("Test".to_string()), None).unwrap();
13181319

13191320
// Wait for payment and verify proof of payment
1320-
match node_a.next_event_async().await {
1321+
let event = node_a.next_event_async().await;
1322+
match &event {
13211323
Event::PaymentSuccessful {
13221324
payment_id: event_payment_id,
13231325
payment_hash,
13241326
payment_preimage,
13251327
fee_paid_msat: _,
13261328
bolt12_invoice,
13271329
} => {
1328-
assert_eq!(event_payment_id, Some(payment_id));
1330+
assert_eq!(*event_payment_id, Some(payment_id));
13291331

13301332
// Verify proof of payment: sha256(preimage) == payment_hash
13311333
let preimage = payment_preimage.expect("preimage should be present");
13321334
let computed_hash = Sha256Hash::hash(&preimage.0);
1333-
assert_eq!(PaymentHash(computed_hash.to_byte_array()), payment_hash);
1335+
assert_eq!(PaymentHash(computed_hash.to_byte_array()), *payment_hash);
13341336

13351337
// Verify the BOLT12 invoice is present and contains the correct payment hash
1336-
let paid_invoice =
1337-
bolt12_invoice.expect("bolt12_invoice should be present for BOLT12 payments");
1338+
let paid_invoice = bolt12_invoice
1339+
.as_ref()
1340+
.expect("bolt12_invoice should be present for BOLT12 payments");
13381341
match paid_invoice {
13391342
PaidBolt12Invoice::Bolt12Invoice(invoice) => {
1340-
assert_eq!(invoice.payment_hash(), payment_hash);
1343+
assert_eq!(invoice.payment_hash(), *payment_hash);
13411344
assert_eq!(invoice.amount_msats(), expected_amount_msat);
13421345
},
13431346
PaidBolt12Invoice::StaticInvoice(_) => {
13441347
panic!("Expected Bolt12Invoice, got StaticInvoice");
13451348
},
13461349
}
13471350

1351+
// Verify serialization round-trip (tests TLV tag 7 backward compatibility)
1352+
let encoded = event.encode();
1353+
let decoded: Event = Readable::read(&mut std::io::Cursor::new(&encoded)).unwrap();
1354+
assert_eq!(event, decoded);
1355+
13481356
node_a.event_handled().unwrap();
13491357
},
1350-
ref e => {
1358+
e => {
13511359
panic!("Unexpected event: {:?}", e);
13521360
},
13531361
}

0 commit comments

Comments
 (0)